@skyramp/mcp 0.3.2 → 0.3.4

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 (181) hide show
  1. package/build/index.js +47 -2
  2. package/build/playwright/PlaywrightTraceService.d.ts +8 -0
  3. package/build/playwright/PlaywrightTraceService.js +1 -0
  4. package/build/playwright/registerPlaywrightTools.js +42 -1
  5. package/build/prompts/enhance-assertions/sharedAssertionRules.js +19 -0
  6. package/build/prompts/pom-aware-code-reuse.js +17 -8
  7. package/build/prompts/test-maintenance/actionsInstructions.js +2 -2
  8. package/build/prompts/test-recommendation/analysisOutputPrompt.js +1 -4
  9. package/build/prompts/test-recommendation/recommendationSections.d.ts +1 -1
  10. package/build/prompts/test-recommendation/recommendationSections.js +5 -5
  11. package/build/prompts/test-recommendation/test-recommendation-prompt.js +13 -7
  12. package/build/prompts/testbot/testbot-prompts.d.ts +2 -12
  13. package/build/prompts/testbot/testbot-prompts.js +28 -16
  14. package/build/recommendation/discriminators.d.ts +7 -1
  15. package/build/recommendation/discriminators.js +16 -3
  16. package/build/resources/testbotResource.js +20 -4
  17. package/build/services/ScenarioGenerationService.js +5 -2
  18. package/build/services/TestExecutionService.d.ts +13 -8
  19. package/build/services/TestExecutionService.js +73 -26
  20. package/build/services/TestGenerationService.js +24 -9
  21. package/build/services/containerEnv.d.ts +12 -1
  22. package/build/services/containerEnv.js +118 -1
  23. package/build/tools/executeSkyrampTestTool.d.ts +9 -0
  24. package/build/tools/executeSkyrampTestTool.js +20 -6
  25. package/build/tools/execution-video-state.d.ts +21 -0
  26. package/build/tools/execution-video-state.js +51 -0
  27. package/build/tools/generate-tests/generateBatchScenarioRestTool.js +31 -11
  28. package/build/tools/generate-tests/planGuard.d.ts +5 -5
  29. package/build/tools/generate-tests/planGuard.js +5 -17
  30. package/build/tools/queryProxyMocksTool.js +0 -1
  31. package/build/tools/submitReportTool.d.ts +83 -10
  32. package/build/tools/submitReportTool.js +179 -29
  33. package/build/tools/test-management/actionsTool.js +52 -41
  34. package/build/tools/test-management/analyzeChangesTool.d.ts +11 -0
  35. package/build/tools/test-management/analyzeChangesTool.js +37 -33
  36. package/build/tools/test-management/analyzeTestHealthTool.js +3 -3
  37. package/build/tools/test-management/registerTestPlanTool.js +113 -31
  38. package/build/types/TestAnalysis.d.ts +7 -3
  39. package/build/types/TestExecution.d.ts +14 -0
  40. package/build/types/TestTypes.js +3 -2
  41. package/build/types/TestbotPromptOptions.d.ts +34 -0
  42. package/build/types/TestbotPromptOptions.js +1 -0
  43. package/build/types/TestbotReport.d.ts +10 -0
  44. package/build/types/TestbotReport.js +10 -1
  45. package/build/types/index.d.ts +2 -0
  46. package/build/types/index.js +1 -0
  47. package/build/utils/AnalysisStateManager.d.ts +36 -2
  48. package/build/utils/AnalysisStateManager.js +34 -13
  49. package/build/utils/frontendSelectors.js +0 -1
  50. package/build/utils/gitStaging.d.ts +5 -0
  51. package/build/utils/gitStaging.js +1 -1
  52. package/build/utils/pom-catalog.d.ts +23 -0
  53. package/build/utils/pom-catalog.js +30 -0
  54. package/build/utils/pom-scope/pom-files.d.ts +14 -0
  55. package/build/utils/pom-scope/pom-files.js +32 -6
  56. package/build/utils/pom-scope/testIdDiscovery.d.ts +40 -0
  57. package/build/utils/pom-scope/testIdDiscovery.js +104 -0
  58. package/build/utils/reportVerification.d.ts +7 -2
  59. package/build/utils/reportVerification.js +9 -3
  60. package/build/utils/scenarioDrafting.js +7 -1
  61. package/build/utils/skyrampMdContent.d.ts +1 -1
  62. package/build/utils/skyrampMdContent.js +1 -1
  63. package/build/utils/urlPath.d.ts +37 -0
  64. package/build/utils/urlPath.js +55 -0
  65. package/build/utils/utils.d.ts +45 -0
  66. package/build/utils/utils.js +50 -0
  67. package/build/utils/versions.d.ts +3 -3
  68. package/build/utils/versions.js +1 -1
  69. package/build/utils/workspaceAuth.d.ts +15 -15
  70. package/build/utils/workspaceAuth.js +32 -17
  71. package/build/workspace/queryParamResolution.d.ts +93 -0
  72. package/build/workspace/queryParamResolution.js +201 -0
  73. package/build/workspace/workspace.d.ts +104 -0
  74. package/build/workspace/workspace.js +24 -0
  75. package/node_modules/playwright/ThirdPartyNotices.txt +319 -266
  76. package/node_modules/playwright/lib/dom-analyzer/blueprint.js +154 -27
  77. package/node_modules/playwright/lib/dom-analyzer/crawler.js +2 -2
  78. package/node_modules/playwright/lib/mcp/browser/tools/pageBlueprint.js +1 -1
  79. package/node_modules/playwright/lib/mcp/browser/tools/sitemap.js +6 -2
  80. package/node_modules/playwright/lib/mcp/skyramp/loadTraceTool.js +3 -2
  81. package/node_modules/playwright/lib/mcp/skyramp/resultCode.js +4 -3
  82. package/node_modules/playwright/lib/mcp/skyramp/skyRampImport.js +15 -3
  83. package/node_modules/playwright/lib/mcp/skyramp/specImport.js +781 -0
  84. package/node_modules/playwright/lib/mcp/skyramp/traceRecordingBackend.js +14 -3
  85. package/node_modules/playwright/lib/mcp/test/resultCode.test.js +2 -1
  86. package/node_modules/playwright/lib/mcp/test/skyRampExport.js +1 -1
  87. package/node_modules/playwright/lib/mcp/test/skyRampExport.test.js +30 -0
  88. package/node_modules/playwright/lib/transform/babelBundleImpl.js +200 -199
  89. package/node_modules/playwright/node_modules/playwright-core/ThirdPartyNotices.txt +3 -3
  90. package/node_modules/playwright/node_modules/playwright-core/bundles/mcp/node_modules/.package-lock.json +3 -3
  91. package/node_modules/playwright/node_modules/playwright-core/bundles/mcp/node_modules/hono/dist/adapter/aws-lambda/handler.js +16 -25
  92. package/node_modules/playwright/node_modules/playwright-core/bundles/mcp/node_modules/hono/dist/adapter/bun/websocket.js +3 -1
  93. package/node_modules/playwright/node_modules/playwright-core/bundles/mcp/node_modules/hono/dist/adapter/lambda-edge/handler.js +20 -4
  94. package/node_modules/playwright/node_modules/playwright-core/bundles/mcp/node_modules/hono/dist/cjs/adapter/aws-lambda/handler.js +16 -25
  95. package/node_modules/playwright/node_modules/playwright-core/bundles/mcp/node_modules/hono/dist/cjs/adapter/bun/websocket.js +3 -1
  96. package/node_modules/playwright/node_modules/playwright-core/bundles/mcp/node_modules/hono/dist/cjs/adapter/lambda-edge/handler.js +20 -4
  97. package/node_modules/playwright/node_modules/playwright-core/bundles/mcp/node_modules/hono/dist/cjs/client/client.js +10 -1
  98. package/node_modules/playwright/node_modules/playwright-core/bundles/mcp/node_modules/hono/dist/cjs/client/utils.js +1 -1
  99. package/node_modules/playwright/node_modules/playwright-core/bundles/mcp/node_modules/hono/dist/cjs/helper/css/common.js +3 -1
  100. package/node_modules/playwright/node_modules/playwright-core/bundles/mcp/node_modules/hono/dist/cjs/helper/css/index.js +9 -1
  101. package/node_modules/playwright/node_modules/playwright-core/bundles/mcp/node_modules/hono/dist/cjs/jsx/base.js +8 -14
  102. package/node_modules/playwright/node_modules/playwright-core/bundles/mcp/node_modules/hono/dist/cjs/jsx/components.js +41 -21
  103. package/node_modules/playwright/node_modules/playwright-core/bundles/mcp/node_modules/hono/dist/cjs/jsx/context.js +131 -5
  104. package/node_modules/playwright/node_modules/playwright-core/bundles/mcp/node_modules/hono/dist/cjs/jsx/streaming.js +9 -7
  105. package/node_modules/playwright/node_modules/playwright-core/bundles/mcp/node_modules/hono/dist/cjs/middleware/bearer-auth/index.js +1 -1
  106. package/node_modules/playwright/node_modules/playwright-core/bundles/mcp/node_modules/hono/dist/cjs/middleware/cache/index.js +1 -1
  107. package/node_modules/playwright/node_modules/playwright-core/bundles/mcp/node_modules/hono/dist/cjs/middleware/compress/index.js +2 -1
  108. package/node_modules/playwright/node_modules/playwright-core/bundles/mcp/node_modules/hono/dist/cjs/middleware/cors/index.js +2 -5
  109. package/node_modules/playwright/node_modules/playwright-core/bundles/mcp/node_modules/hono/dist/cjs/middleware/etag/index.js +2 -1
  110. package/node_modules/playwright/node_modules/playwright-core/bundles/mcp/node_modules/hono/dist/cjs/middleware/language/language.js +10 -32
  111. package/node_modules/playwright/node_modules/playwright-core/bundles/mcp/node_modules/hono/dist/cjs/middleware/method-override/index.js +5 -3
  112. package/node_modules/playwright/node_modules/playwright-core/bundles/mcp/node_modules/hono/dist/cjs/middleware/serve-static/index.js +2 -2
  113. package/node_modules/playwright/node_modules/playwright-core/bundles/mcp/node_modules/hono/dist/cjs/middleware/timing/timing.js +3 -1
  114. package/node_modules/playwright/node_modules/playwright-core/bundles/mcp/node_modules/hono/dist/cjs/router/trie-router/node.js +9 -0
  115. package/node_modules/playwright/node_modules/playwright-core/bundles/mcp/node_modules/hono/dist/cjs/utils/body.js +12 -4
  116. package/node_modules/playwright/node_modules/playwright-core/bundles/mcp/node_modules/hono/dist/cjs/utils/buffer.js +2 -1
  117. package/node_modules/playwright/node_modules/playwright-core/bundles/mcp/node_modules/hono/dist/cjs/utils/ipaddr.js +6 -1
  118. package/node_modules/playwright/node_modules/playwright-core/bundles/mcp/node_modules/hono/dist/cjs/validator/validator.js +3 -3
  119. package/node_modules/playwright/node_modules/playwright-core/bundles/mcp/node_modules/hono/dist/client/client.js +10 -1
  120. package/node_modules/playwright/node_modules/playwright-core/bundles/mcp/node_modules/hono/dist/client/utils.js +1 -1
  121. package/node_modules/playwright/node_modules/playwright-core/bundles/mcp/node_modules/hono/dist/helper/css/common.js +3 -1
  122. package/node_modules/playwright/node_modules/playwright-core/bundles/mcp/node_modules/hono/dist/helper/css/index.js +9 -1
  123. package/node_modules/playwright/node_modules/playwright-core/bundles/mcp/node_modules/hono/dist/jsx/base.js +15 -15
  124. package/node_modules/playwright/node_modules/playwright-core/bundles/mcp/node_modules/hono/dist/jsx/components.js +42 -22
  125. package/node_modules/playwright/node_modules/playwright-core/bundles/mcp/node_modules/hono/dist/jsx/context.js +129 -5
  126. package/node_modules/playwright/node_modules/playwright-core/bundles/mcp/node_modules/hono/dist/jsx/streaming.js +10 -8
  127. package/node_modules/playwright/node_modules/playwright-core/bundles/mcp/node_modules/hono/dist/middleware/bearer-auth/index.js +1 -1
  128. package/node_modules/playwright/node_modules/playwright-core/bundles/mcp/node_modules/hono/dist/middleware/cache/index.js +1 -1
  129. package/node_modules/playwright/node_modules/playwright-core/bundles/mcp/node_modules/hono/dist/middleware/compress/index.js +2 -1
  130. package/node_modules/playwright/node_modules/playwright-core/bundles/mcp/node_modules/hono/dist/middleware/cors/index.js +2 -5
  131. package/node_modules/playwright/node_modules/playwright-core/bundles/mcp/node_modules/hono/dist/middleware/etag/index.js +2 -1
  132. package/node_modules/playwright/node_modules/playwright-core/bundles/mcp/node_modules/hono/dist/middleware/language/language.js +10 -32
  133. package/node_modules/playwright/node_modules/playwright-core/bundles/mcp/node_modules/hono/dist/middleware/method-override/index.js +5 -3
  134. package/node_modules/playwright/node_modules/playwright-core/bundles/mcp/node_modules/hono/dist/middleware/serve-static/index.js +2 -2
  135. package/node_modules/playwright/node_modules/playwright-core/bundles/mcp/node_modules/hono/dist/middleware/timing/timing.js +3 -1
  136. package/node_modules/playwright/node_modules/playwright-core/bundles/mcp/node_modules/hono/dist/router/trie-router/node.js +9 -0
  137. package/node_modules/playwright/node_modules/playwright-core/bundles/mcp/node_modules/hono/dist/types/adapter/aws-lambda/handler.d.ts +1 -1
  138. package/node_modules/playwright/node_modules/playwright-core/bundles/mcp/node_modules/hono/dist/types/adapter/lambda-edge/handler.d.ts +1 -1
  139. package/node_modules/playwright/node_modules/playwright-core/bundles/mcp/node_modules/hono/dist/types/helper/websocket/index.d.ts +1 -1
  140. package/node_modules/playwright/node_modules/playwright-core/bundles/mcp/node_modules/hono/dist/types/jsx/base.d.ts +1 -3
  141. package/node_modules/playwright/node_modules/playwright-core/bundles/mcp/node_modules/hono/dist/types/jsx/context.d.ts +39 -0
  142. package/node_modules/playwright/node_modules/playwright-core/bundles/mcp/node_modules/hono/dist/types/middleware/context-storage/index.d.ts +2 -2
  143. package/node_modules/playwright/node_modules/playwright-core/bundles/mcp/node_modules/hono/dist/types/middleware/language/language.d.ts +18 -0
  144. package/node_modules/playwright/node_modules/playwright-core/bundles/mcp/node_modules/hono/dist/types/utils/body.d.ts +1 -1
  145. package/node_modules/playwright/node_modules/playwright-core/bundles/mcp/node_modules/hono/dist/types/utils/types.d.ts +1 -1
  146. package/node_modules/playwright/node_modules/playwright-core/bundles/mcp/node_modules/hono/dist/utils/body.js +12 -4
  147. package/node_modules/playwright/node_modules/playwright-core/bundles/mcp/node_modules/hono/dist/utils/buffer.js +2 -1
  148. package/node_modules/playwright/node_modules/playwright-core/bundles/mcp/node_modules/hono/dist/utils/ipaddr.js +6 -1
  149. package/node_modules/playwright/node_modules/playwright-core/bundles/mcp/node_modules/hono/dist/validator/validator.js +3 -3
  150. package/node_modules/playwright/node_modules/playwright-core/bundles/mcp/node_modules/hono/package.json +29 -22
  151. package/node_modules/playwright/node_modules/playwright-core/bundles/mcp/package-lock.json +3 -3
  152. package/node_modules/playwright/node_modules/playwright-core/lib/generated/pollingRecorderSource.js +1 -1
  153. package/node_modules/playwright/node_modules/playwright-core/lib/server/codegen/skyramp/jsonlReader.js +5 -1
  154. package/node_modules/playwright/node_modules/playwright-core/lib/server/codegen/skyramp/specReader.js +781 -0
  155. package/node_modules/playwright/node_modules/playwright-core/lib/server/recorder/recorderApp.js +25 -6
  156. package/node_modules/playwright/node_modules/playwright-core/lib/server/recorder/skyramp/replayEngine.js +3 -1
  157. package/node_modules/playwright/node_modules/playwright-core/lib/utils.js +2 -0
  158. package/node_modules/playwright/node_modules/playwright-core/lib/vite/htmlReport/index.html +253 -27
  159. package/node_modules/playwright/node_modules/playwright-core/lib/vite/recorder/assets/{codeMirrorModule-D0BjbCb7.js → codeMirrorModule-DtudTj_v.js} +1 -1
  160. package/node_modules/playwright/node_modules/playwright-core/lib/vite/recorder/assets/index-BpDwp16L.js +422 -0
  161. package/node_modules/playwright/node_modules/playwright-core/lib/vite/recorder/index.html +1 -1
  162. package/node_modules/playwright/node_modules/playwright-core/lib/vite/traceViewer/assets/{codeMirrorModule-Bzd72-bG.js → codeMirrorModule-FNMuBzX1.js} +1 -1
  163. package/node_modules/playwright/node_modules/playwright-core/lib/vite/traceViewer/assets/defaultSettingsView-Co9upU5h.js +1035 -0
  164. package/node_modules/playwright/node_modules/playwright-core/lib/vite/traceViewer/index.DXNIQ_dx.js +2 -0
  165. package/node_modules/playwright/node_modules/playwright-core/lib/vite/traceViewer/index.html +2 -2
  166. package/node_modules/playwright/node_modules/playwright-core/lib/vite/traceViewer/uiMode.CIKB3XSv.js +5 -0
  167. package/node_modules/playwright/node_modules/playwright-core/lib/vite/traceViewer/uiMode.html +2 -2
  168. package/node_modules/playwright/node_modules/playwright-core/package.json +1 -1
  169. package/node_modules/playwright/node_modules/playwright-core/src/generated/pollingRecorderSource.ts +1 -1
  170. package/node_modules/playwright/node_modules/playwright-core/src/server/codegen/skyramp/jsonlReader.ts +4 -0
  171. package/node_modules/playwright/node_modules/playwright-core/src/server/codegen/skyramp/specReader.ts +1028 -0
  172. package/node_modules/playwright/node_modules/playwright-core/src/server/recorder/recorderApp.ts +31 -8
  173. package/node_modules/playwright/node_modules/playwright-core/src/server/recorder/skyramp/replayEngine.ts +1 -0
  174. package/node_modules/playwright/node_modules/playwright-core/src/utils.ts +1 -0
  175. package/node_modules/playwright/package.json +1 -1
  176. package/package.json +10 -6
  177. package/node_modules/playwright/node_modules/playwright-core/bundles/mcp/node_modules/hono/dist/tsconfig.build.tsbuildinfo +0 -1
  178. package/node_modules/playwright/node_modules/playwright-core/lib/vite/recorder/assets/index-lvTRGFx-.js +0 -193
  179. package/node_modules/playwright/node_modules/playwright-core/lib/vite/traceViewer/assets/defaultSettingsView-DzxTioTK.js +0 -809
  180. package/node_modules/playwright/node_modules/playwright-core/lib/vite/traceViewer/index.BGc30U3S.js +0 -2
  181. package/node_modules/playwright/node_modules/playwright-core/lib/vite/traceViewer/uiMode.IaDrb29A.js +0 -5
@@ -1 +1 @@
1
- export const source = "\nvar __commonJS = obj => {\n let required = false;\n let result;\n return function __require() {\n if (!required) {\n required = true;\n let fn;\n for (const name in obj) { fn = obj[name]; break; }\n const module = { exports: {} };\n fn(module.exports, module);\n result = module.exports;\n }\n return result;\n }\n};\nvar __export = (target, all) => {for (var name in all) target[name] = all[name];};\nvar __toESM = mod => ({ ...mod, 'default': mod });\nvar __toCommonJS = mod => ({ ...mod, __esModule: true });\n\n\n// packages/injected/src/recorder/pollingRecorder.ts\nvar pollingRecorder_exports = {};\n__export(pollingRecorder_exports, {\n PollingRecorder: () => PollingRecorder,\n default: () => pollingRecorder_default\n});\nmodule.exports = __toCommonJS(pollingRecorder_exports);\n\n// packages/injected/src/recorder/clipPaths.ts\nvar svgJson = { \"tagName\": \"svg\", \"children\": [{ \"tagName\": \"defs\", \"children\": [{ \"tagName\": \"clipPath\", \"attrs\": { \"width\": \"16\", \"height\": \"16\", \"viewBox\": \"0 0 16 16\", \"fill\": \"currentColor\", \"id\": \"icon-gripper\" }, \"children\": [{ \"tagName\": \"path\", \"attrs\": { \"d\": \"M5 3h2v2H5zm0 4h2v2H5zm0 4h2v2H5zm4-8h2v2H9zm0 4h2v2H9zm0 4h2v2H9z\" } }] }, { \"tagName\": \"clipPath\", \"attrs\": { \"width\": \"16\", \"height\": \"16\", \"viewBox\": \"0 0 16 16\", \"fill\": \"currentColor\", \"id\": \"icon-circle-large-filled\" }, \"children\": [{ \"tagName\": \"path\", \"attrs\": { \"d\": \"M8 1a6.8 6.8 0 0 1 1.86.253 6.899 6.899 0 0 1 3.083 1.805 6.903 6.903 0 0 1 1.804 3.083C14.916 6.738 15 7.357 15 8s-.084 1.262-.253 1.86a6.9 6.9 0 0 1-.704 1.674 7.157 7.157 0 0 1-2.516 2.509 6.966 6.966 0 0 1-1.668.71A6.984 6.984 0 0 1 8 15a6.984 6.984 0 0 1-1.86-.246 7.098 7.098 0 0 1-1.674-.711 7.3 7.3 0 0 1-1.415-1.094 7.295 7.295 0 0 1-1.094-1.415 7.098 7.098 0 0 1-.71-1.675A6.985 6.985 0 0 1 1 8c0-.643.082-1.262.246-1.86a6.968 6.968 0 0 1 .711-1.667 7.156 7.156 0 0 1 2.509-2.516 6.895 6.895 0 0 1 1.675-.704A6.808 6.808 0 0 1 8 1z\" } }] }, { \"tagName\": \"clipPath\", \"attrs\": { \"width\": \"16\", \"height\": \"16\", \"viewBox\": \"0 0 16 16\", \"fill\": \"currentColor\", \"id\": \"icon-stop-circle\" }, \"children\": [{ \"tagName\": \"path\", \"attrs\": { \"d\": \"M6 6h4v4H6z\" } }, { \"tagName\": \"path\", \"attrs\": { \"fill-rule\": \"evenodd\", \"clip-rule\": \"evenodd\", \"d\": \"M8.6 1c1.6.1 3.1.9 4.2 2 1.3 1.4 2 3.1 2 5.1 0 1.6-.6 3.1-1.6 4.4-1 1.2-2.4 2.1-4 2.4-1.6.3-3.2.1-4.6-.7-1.4-.8-2.5-2-3.1-3.5C.9 9.2.8 7.5 1.3 6c.5-1.6 1.4-2.9 2.8-3.8C5.4 1.3 7 .9 8.6 1zm.5 12.9c1.3-.3 2.5-1 3.4-2.1.8-1.1 1.3-2.4 1.2-3.8 0-1.6-.6-3.2-1.7-4.3-1-1-2.2-1.6-3.6-1.7-1.3-.1-2.7.2-3.8 1-1.1.8-1.9 1.9-2.3 3.3-.4 1.3-.4 2.7.2 4 .6 1.3 1.5 2.3 2.7 3 1.2.7 2.6.9 3.9.6z\" } }] }, { \"tagName\": \"clipPath\", \"attrs\": { \"width\": \"16\", \"height\": \"16\", \"viewBox\": \"0 0 16 16\", \"fill\": \"currentColor\", \"id\": \"icon-inspect\" }, \"children\": [{ \"tagName\": \"path\", \"attrs\": { \"fill-rule\": \"evenodd\", \"clip-rule\": \"evenodd\", \"d\": \"M1 3l1-1h12l1 1v6h-1V3H2v8h5v1H2l-1-1V3zm14.707 9.707L9 6v9.414l2.707-2.707h4zM10 13V8.414l3.293 3.293h-2L10 13z\" } }] }, { \"tagName\": \"clipPath\", \"attrs\": { \"width\": \"16\", \"height\": \"16\", \"viewBox\": \"0 0 16 16\", \"fill\": \"currentColor\", \"id\": \"icon-whole-word\" }, \"children\": [{ \"tagName\": \"path\", \"attrs\": { \"fill-rule\": \"evenodd\", \"clip-rule\": \"evenodd\", \"d\": \"M0 11H1V13H15V11H16V14H15H1H0V11Z\" } }, { \"tagName\": \"path\", \"attrs\": { \"d\": \"M6.84048 11H5.95963V10.1406H5.93814C5.555 10.7995 4.99104 11.1289 4.24625 11.1289C3.69839 11.1289 3.26871 10.9839 2.95718 10.6938C2.64924 10.4038 2.49527 10.0189 2.49527 9.53906C2.49527 8.51139 3.10041 7.91341 4.3107 7.74512L5.95963 7.51416C5.95963 6.57959 5.58186 6.1123 4.82632 6.1123C4.16389 6.1123 3.56591 6.33789 3.03238 6.78906V5.88672C3.57307 5.54297 4.19612 5.37109 4.90152 5.37109C6.19416 5.37109 6.84048 6.05501 6.84048 7.42285V11ZM5.95963 8.21777L4.63297 8.40039C4.22476 8.45768 3.91682 8.55973 3.70914 8.70654C3.50145 8.84977 3.39761 9.10579 3.39761 9.47461C3.39761 9.74316 3.4925 9.96338 3.68228 10.1353C3.87564 10.3035 4.13166 10.3877 4.45035 10.3877C4.8872 10.3877 5.24706 10.2355 5.52994 9.93115C5.8164 9.62321 5.95963 9.2347 5.95963 8.76562V8.21777Z\" } }, { \"tagName\": \"path\", \"attrs\": { \"d\": \"M9.3475 10.2051H9.32601V11H8.44515V2.85742H9.32601V6.4668H9.3475C9.78076 5.73633 10.4146 5.37109 11.2489 5.37109C11.9543 5.37109 12.5057 5.61816 12.9032 6.1123C13.3042 6.60286 13.5047 7.26172 13.5047 8.08887C13.5047 9.00911 13.2809 9.74674 12.8333 10.3018C12.3857 10.8532 11.7734 11.1289 10.9964 11.1289C10.2695 11.1289 9.71989 10.821 9.3475 10.2051ZM9.32601 7.98682V8.75488C9.32601 9.20964 9.47282 9.59635 9.76644 9.91504C10.0636 10.2301 10.4396 10.3877 10.8944 10.3877C11.4279 10.3877 11.8451 10.1836 12.1458 9.77539C12.4502 9.36719 12.6024 8.79964 12.6024 8.07275C12.6024 7.46045 12.4609 6.98063 12.1781 6.6333C11.8952 6.28597 11.512 6.1123 11.0286 6.1123C10.5166 6.1123 10.1048 6.29134 9.7933 6.64941C9.48177 7.00391 9.32601 7.44971 9.32601 7.98682Z\" } }] }, { \"tagName\": \"clipPath\", \"attrs\": { \"width\": \"16\", \"height\": \"16\", \"viewBox\": \"0 0 16 16\", \"fill\": \"currentColor\", \"id\": \"icon-eye\" }, \"children\": [{ \"tagName\": \"path\", \"attrs\": { \"d\": \"M7.99993 6.00316C9.47266 6.00316 10.6666 7.19708 10.6666 8.66981C10.6666 10.1426 9.47266 11.3365 7.99993 11.3365C6.52715 11.3365 5.33324 10.1426 5.33324 8.66981C5.33324 7.19708 6.52715 6.00316 7.99993 6.00316ZM7.99993 7.00315C7.07946 7.00315 6.33324 7.74935 6.33324 8.66981C6.33324 9.59028 7.07946 10.3365 7.99993 10.3365C8.9204 10.3365 9.6666 9.59028 9.6666 8.66981C9.6666 7.74935 8.9204 7.00315 7.99993 7.00315ZM7.99993 3.66675C11.0756 3.66675 13.7307 5.76675 14.4673 8.70968C14.5344 8.97755 14.3716 9.24908 14.1037 9.31615C13.8358 9.38315 13.5643 9.22041 13.4973 8.95248C12.8713 6.45205 10.6141 4.66675 7.99993 4.66675C5.38454 4.66675 3.12664 6.45359 2.50182 8.95555C2.43491 9.22341 2.16348 9.38635 1.89557 9.31948C1.62766 9.25255 1.46471 8.98115 1.53162 8.71321C2.26701 5.76856 4.9229 3.66675 7.99993 3.66675Z\" } }] }, { \"tagName\": \"clipPath\", \"attrs\": { \"width\": \"16\", \"height\": \"16\", \"viewBox\": \"0 0 16 16\", \"fill\": \"currentColor\", \"id\": \"icon-symbol-constant\" }, \"children\": [{ \"tagName\": \"path\", \"attrs\": { \"fill-rule\": \"evenodd\", \"clip-rule\": \"evenodd\", \"d\": \"M4 6h8v1H4V6zm8 3H4v1h8V9z\" } }, { \"tagName\": \"path\", \"attrs\": { \"fill-rule\": \"evenodd\", \"clip-rule\": \"evenodd\", \"d\": \"M1 4l1-1h12l1 1v8l-1 1H2l-1-1V4zm1 0v8h12V4H2z\" } }] }, { \"tagName\": \"clipPath\", \"attrs\": { \"width\": \"16\", \"height\": \"16\", \"viewBox\": \"0 0 16 16\", \"fill\": \"currentColor\", \"id\": \"icon-check\" }, \"children\": [{ \"tagName\": \"path\", \"attrs\": { \"fill-rule\": \"evenodd\", \"clip-rule\": \"evenodd\", \"d\": \"M14.431 3.323l-8.47 10-.79-.036-3.35-4.77.818-.574 2.978 4.24 8.051-9.506.764.646z\" } }] }, { \"tagName\": \"clipPath\", \"attrs\": { \"width\": \"16\", \"height\": \"16\", \"viewBox\": \"0 0 16 16\", \"fill\": \"none\", \"stroke\": \"currentColor\", \"stroke-linecap\": \"round\", \"stroke-linejoin\": \"round\", \"stroke-width\": \"1\", \"id\": \"icon-location-pin\" }, \"children\": [{ \"tagName\": \"path\", \"attrs\": { \"d\": \"\\n M8 1\\n C5.243 1 3 3.243 3 6\\n C3 9 8 14 8 14\\n C8 14 13 9 13 6\\n C13 3.243 10.757 1 8 1\\n Z\\n M6 6\\n A2 2 0 1 1 10 6\\n A2 2 0 1 1 6 6\\n Z\\n \" } }] }, { \"tagName\": \"clipPath\", \"attrs\": { \"width\": \"16\", \"height\": \"16\", \"viewBox\": \"0 0 16 16\", \"fill\": \"currentColor\", \"id\": \"icon-layers\" }, \"children\": [{ \"tagName\": \"path\", \"attrs\": { \"d\": \"M8 2L2 5v1l6 3 6-3V5L8 2zm0 1.18L11.82 5 8 6.82 4.18 5 8 3.18zM2 7.13V8l6 3 6-3v-.87L8 10.2 2 7.13zM2 10.13V11l6 3 6-3v-.87L8 13.2 2 10.13z\" } }] }, { \"tagName\": \"clipPath\", \"attrs\": { \"width\": \"16\", \"height\": \"16\", \"viewBox\": \"0 0 16 16\", \"fill\": \"currentColor\", \"id\": \"icon-list-tree\" }, \"children\": [{ \"tagName\": \"path\", \"attrs\": { \"d\": \"M2 3.5C2 3.22386 2.22386 3 2.5 3H13.5C13.7761 3 14 3.22386 14 3.5C14 3.77614 13.7761 4 13.5 4H6V6H13.5C13.7761 6 14 6.22386 14 6.5C14 6.77614 13.7761 7 13.5 7H6V9H13.5C13.7761 9 14 9.22386 14 9.5C14 9.77614 13.7761 10 13.5 10H6V12H13.5C13.7761 12 14 12.2239 14 12.5C14 12.7761 13.7761 13 13.5 13H5.5C5.22386 13 5 12.7761 5 12.5V4H2.5C2.22386 4 2 3.77614 2 3.5Z\" } }] }, { \"tagName\": \"clipPath\", \"attrs\": { \"width\": \"16\", \"height\": \"16\", \"viewBox\": \"0 0 16 16\", \"fill\": \"currentColor\", \"id\": \"icon-brackets\" }, \"children\": [{ \"tagName\": \"path\", \"attrs\": { \"d\": \"M4.5 2H2v12h2.5v-1H3V3h1.5V2zm7 0H14v12h-2.5v-1H13V3h-1.5V2zM6 5h4v1H6V5zm0 3h4v1H6V8zm0 3h4v1H6v-1z\" } }] }, { \"tagName\": \"clipPath\", \"attrs\": { \"width\": \"16\", \"height\": \"16\", \"viewBox\": \"0 0 16 16\", \"fill\": \"currentColor\", \"id\": \"icon-braces\" }, \"children\": [{ \"tagName\": \"path\", \"attrs\": { \"fill-rule\": \"evenodd\", \"clip-rule\": \"evenodd\", \"d\": \"M3 3.5C3 2.67 3.67 2 4.5 2H5v1h-.5c-.28 0-.5.22-.5.5v3c0 .83-.67 1.5-1.5 1.5.83 0 1.5.67 1.5 1.5v3c0 .28.22.5.5.5H5v1h-.5C3.67 14 3 13.33 3 12.5v-3c0-.28-.22-.5-.5-.5H2V8h.5c.28 0 .5-.22.5-.5v-3zm10 0C13 2.67 12.33 2 11.5 2H11v1h.5c.28 0 .5.22.5.5v3c0 .83.67 1.5 1.5 1.5-.83 0-1.5.67-1.5 1.5v3c0 .28-.22.5-.5.5H11v1h.5c.83 0 1.5-.67 1.5-1.5v-3c0-.28.22-.5.5-.5H14V8h-.5c-.28 0-.5-.22-.5-.5v-3z\" } }] }, { \"tagName\": \"clipPath\", \"attrs\": { \"width\": \"16\", \"height\": \"16\", \"viewBox\": \"0 0 16 16\", \"fill\": \"currentColor\", \"id\": \"icon-braces-dashes\" }, \"children\": [{ \"tagName\": \"path\", \"attrs\": { \"d\": \"M3 3.5C3 2.67 3.67 2 4.5 2H5v1h-.5c-.28 0-.5.22-.5.5v3c0 .83-.67 1.5-1.5 1.5.83 0 1.5.67 1.5 1.5v3c0 .28.22.5.5.5H5v1h-.5C3.67 14 3 13.33 3 12.5v-3c0-.28-.22-.5-.5-.5H2V8h.5c.28 0 .5-.22.5-.5v-3zm10 0C13 2.67 12.33 2 11.5 2H11v1h.5c.28 0 .5.22.5.5v3c0 .83.67 1.5 1.5 1.5-.83 0-1.5.67-1.5 1.5v3c0 .28-.22.5-.5.5H11v1h.5c.83 0 1.5-.67 1.5-1.5v-3c0-.28.22-.5.5-.5H14V8h-.5c-.28 0-.5-.22-.5-.5v-3zM6 5h4v1H6V5zm0 3h4v1H6V8zm0 3h4v1H6v-1z\" } }] }, { \"tagName\": \"clipPath\", \"attrs\": { \"width\": \"16\", \"height\": \"16\", \"viewBox\": \"0 0 16 16\", \"fill\": \"currentColor\", \"id\": \"icon-close\" }, \"children\": [{ \"tagName\": \"path\", \"attrs\": { \"fill-rule\": \"evenodd\", \"clip-rule\": \"evenodd\", \"d\": \"M8 8.707l3.646 3.647.708-.707L8.707 8l3.647-3.646-.707-.708L8 7.293 4.354 3.646l-.707.708L7.293 8l-3.646 3.646.707.708L8 8.707z\" } }] }, { \"tagName\": \"clipPath\", \"attrs\": { \"width\": \"16\", \"height\": \"16\", \"viewBox\": \"0 0 16 16\", \"fill\": \"currentColor\", \"id\": \"icon-pass\" }, \"children\": [{ \"tagName\": \"path\", \"attrs\": { \"d\": \"M6.27 10.87h.71l4.56-4.56-.71-.71-4.2 4.21-1.92-1.92L4 8.6l2.27 2.27z\" } }, { \"tagName\": \"path\", \"attrs\": { \"fill-rule\": \"evenodd\", \"clip-rule\": \"evenodd\", \"d\": \"M8.6 1c1.6.1 3.1.9 4.2 2 1.3 1.4 2 3.1 2 5.1 0 1.6-.6 3.1-1.6 4.4-1 1.2-2.4 2.1-4 2.4-1.6.3-3.2.1-4.6-.7-1.4-.8-2.5-2-3.1-3.5C.9 9.2.8 7.5 1.3 6c.5-1.6 1.4-2.9 2.8-3.8C5.4 1.3 7 .9 8.6 1zm.5 12.9c1.3-.3 2.5-1 3.4-2.1.8-1.1 1.3-2.4 1.2-3.8 0-1.6-.6-3.2-1.7-4.3-1-1-2.2-1.6-3.6-1.7-1.3-.1-2.7.2-3.8 1-1.1.8-1.9 1.9-2.3 3.3-.4 1.3-.4 2.7.2 4 .6 1.3 1.5 2.3 2.7 3 1.2.7 2.6.9 3.9.6z\" } }] }, { \"tagName\": \"clipPath\", \"attrs\": { \"width\": \"16\", \"height\": \"16\", \"viewBox\": \"0 0 16 16\", \"fill\": \"currentColor\", \"id\": \"icon-gist\" }, \"children\": [{ \"tagName\": \"path\", \"attrs\": { \"fill-rule\": \"evenodd\", \"clip-rule\": \"evenodd\", \"d\": \"M10.57 1.14l3.28 3.3.15.36v9.7l-.5.5h-11l-.5-.5v-13l.5-.5h7.72l.35.14zM10 5h3l-3-3v3zM3 2v12h10V6H9.5L9 5.5V2H3zm2.062 7.533l1.817-1.828L6.17 7 4 9.179v.707l2.171 2.174.707-.707-1.816-1.82zM8.8 7.714l.7-.709 2.189 2.175v.709L9.5 12.062l-.705-.709 1.831-1.82L8.8 7.714z\" } }] }, { \"tagName\": \"clipPath\", \"attrs\": { \"width\": \"16\", \"height\": \"16\", \"viewBox\": \"0 0 16 16\", \"fill\": \"currentColor\", \"id\": \"icon-snapshot\" }, \"children\": [{ \"tagName\": \"path\", \"attrs\": { \"d\": \"M2 1.5l.5-.5h7.72l.35.14 3.28 3.3.15.36v9.7l-.5.5h-11l-.5-.5v-13zm1 .5v12h10V6H9.5L9 5.5V2H3zm7 0v3h3l-3-3z\" } }, { \"tagName\": \"path\", \"attrs\": { \"fill\": \"none\", \"stroke\": \"currentColor\", \"stroke-width\": \"0.8\", \"transform\": \"rotate(7 8 8.7)\", \"d\": \"M 10 5.2 C 8 5.2, 6 5.7, 6 7.2 C 6 8.7, 7.5 8.7, 8 8.7 C 8.5 8.7, 10 8.7, 10 10.2 C 10 11.7, 8 12.2, 6 12.2\" } }] }, { \"tagName\": \"clipPath\", \"attrs\": { \"width\": \"16\", \"height\": \"16\", \"viewBox\": \"0 0 16 16\", \"fill\": \"currentColor\", \"id\": \"icon-move\" }, \"children\": [{ \"tagName\": \"path\", \"attrs\": { \"fill-rule\": \"evenodd\", \"clip-rule\": \"evenodd\", \"d\": \"M8.5 2.5V5.5H7.5V2.5L6.20711 3.79289L5.5 3.08579L8 0.585786L10.5 3.08579L9.79289 3.79289L8.5 2.5ZM7.5 10.5V13.5L6.20711 12.2071L5.5 12.9142L8 15.4142L10.5 12.9142L9.79289 12.2071L8.5 13.5V10.5H7.5ZM10.5 8.5H13.5L12.2071 9.79289L12.9142 10.5L15.4142 8L12.9142 5.5L12.2071 6.20711L13.5 7.5H10.5V8.5ZM5.5 7.5H2.5L3.79289 6.20711L3.08579 5.5L0.585786 8L3.08579 10.5L3.79289 9.79289L2.5 8.5H5.5V7.5Z\" } }] }, { \"tagName\": \"clipPath\", \"attrs\": { \"width\": \"16\", \"height\": \"16\", \"viewBox\": \"0 0 16 16\", \"fill\": \"currentColor\", \"id\": \"icon-selection\" }, \"children\": [{ \"tagName\": \"path\", \"attrs\": { \"fill-rule\": \"evenodd\", \"clip-rule\": \"evenodd\", \"d\": \"M1 1H3V2H2V3H1V1ZM4 1H6V2H4V1ZM7 1H9V2H7V1ZM10 1H12V2H10V1ZM13 1H15V3H14V2H13V1ZM14 4H15V6H14V4ZM14 7H15V9H14V7ZM14 10H15V12H14V10ZM13 13V14H14V15H13H12V14H13V13H14V12H15V13V15H13ZM10 14H12V15H10V14ZM7 14H9V15H7V14ZM4 14H6V15H4V14ZM1 13H2V14H3V15H1V13ZM1 10H2V12H1V10ZM1 7H2V9H1V7ZM1 4H2V6H1V4Z\" } }] }, { \"tagName\": \"clipPath\", \"attrs\": { \"viewBox\": \"0 0 16 16\", \"fill\": \"currentColor\", \"id\": \"icon-table\" }, \"children\": [{ \"tagName\": \"path\", \"attrs\": { \"fill-rule\": \"evenodd\", \"d\": \"M2 3h12v10H2V3zm1 1v8h10V4H3z\" } }, { \"tagName\": \"rect\", \"attrs\": { \"x\": \"2\", \"y\": \"6.33\", \"width\": \"12\", \"height\": \"1\" } }, { \"tagName\": \"rect\", \"attrs\": { \"x\": \"2\", \"y\": \"9.67\", \"width\": \"12\", \"height\": \"1\" } }, { \"tagName\": \"rect\", \"attrs\": { \"x\": \"5.67\", \"y\": \"3\", \"width\": \"1\", \"height\": \"10\" } }, { \"tagName\": \"rect\", \"attrs\": { \"x\": \"9.33\", \"y\": \"3\", \"width\": \"1\", \"height\": \"10\" } }] }, { \"tagName\": \"clipPath\", \"attrs\": { \"viewBox\": \"0 0 16 16\", \"fill\": \"currentColor\", \"id\": \"icon-file-upload\" }, \"children\": [{ \"tagName\": \"path\", \"attrs\": { \"d\": \"M14.5 3H7.71l-.85-.85L6.51 2h-5l-.5.5v11l.5.5h13l.5-.5v-10L14.5 3zm-.51 8.49V13h-12V7h4.49l.35-.15.86-.86H14v1.5l.001 4zm0-6.49h-6.5l-.35.15-.86.86H2v-3h4.29l.85.85.36.15H14l-.01.99z\" } }] }, { \"tagName\": \"clipPath\", \"attrs\": { \"viewBox\": \"0 0 16 16\", \"fill\": \"currentColor\", \"id\": \"icon-eraser\" }, \"children\": [{ \"tagName\": \"path\", \"attrs\": { \"d\": \"M 13.54 2.70 L 11.30 0.46 C 10.68 -0.15 9.68 -0.15 9.06 0.46 L 3.34 6.18 L 1.46 8.07 C 0.85 8.68 0.85 9.68 1.46 10.30 L 3.70 12.54 C 4.01 12.84 4.41 13 4.82 13 C 5.22 13 5.63 12.84 5.93 12.54 L 7.82 10.66 L 8.26 10.21 L 13.54 4.93 C 13.84 4.64 14 4.24 14 3.82 C 14 3.39 13.84 3.00 13.54 2.70 Z M 7.37 10.21 L 5.49 12.09 C 5.12 12.46 4.51 12.46 4.15 12.09 L 1.91 9.85 C 1.54 9.48 1.54 8.88 1.91 8.51 L 3.79 6.63 L 7.37 10.21 Z M 13.09 4.49 L 7.82 9.76 L 6.03 7.97 L 4.24 6.18 L 9.51 0.91 C 9.88 0.54 10.48 0.54 10.85 0.91 L 13.09 3.15 C 13.27 3.33 13.37 3.56 13.37 3.82 C 13.37 4.07 13.27 4.31 13.09 4.49 Z\" } }, { \"tagName\": \"rect\", \"attrs\": { \"x\": \"1\", \"y\": \"13.2\", \"width\": \"12\", \"height\": \"1.6\" } }] }, { \"tagName\": \"clipPath\", \"attrs\": { \"viewBox\": \"0 0 100 100\", \"fill\": \"currentColor\", \"id\": \"icon-sketch-tool\" }, \"children\": [{ \"tagName\": \"path\", \"attrs\": { \"d\": \"m37.68 70.594c-0.36328 0.75781-0.66406 1.5508-0.89063 2.3594-0.41406 1.4727-0.58984 3.0195-0.51562 4.5742 0.042968 0.95312-0.58984 1.7852-1.4766 2.0195l-21.281 5.707c-1.0664 0.28516-2.1602-0.34766-2.4453-1.4141-0.09375-0.35156-0.085937-0.70703 0-1.0312l5.7031-21.285c0.25781-0.96094 1.1719-1.5703 2.1289-1.4727 1.5195 0.0625 3.0273-0.11719 4.4688-0.51953 0.80859-0.22656 1.5977-0.52734 2.3594-0.89453-0.64453-0.78516-0.60156-1.9453 0.13281-2.6797l3.7227-3.7227c0.70703-0.70703 1.8086-0.77344 2.5898-0.20312l35.953-35.953c-0.57031-0.78125-0.50391-1.8828 0.20312-2.5898l3.7227-3.7227c0.70703-0.70703 1.8086-0.77344 2.5898-0.20312l6.5039-6.5039c0.78125-0.78125 2.0469-0.78125 2.8281 0l9.2891 9.2891c0.78125 0.78125 0.78125 2.0469 0 2.8281l-6.5039 6.5039c0.57031 0.78125 0.5 1.8828-0.20312 2.5859l-0.49219 0.49219 2.875 2.875c0.78125 0.78125 0.78125 2.0469 0 2.8281l-16.711 16.711c-0.78125 0.78125-2.0469 0.78125-2.8281 0s-0.78125-2.0469 0-2.8281l15.297-15.297-1.4609-1.4609-0.40234 0.40234c-0.70703 0.70703-1.8047 0.77344-2.5859 0.20312l-35.953 35.953c0.56641 0.78125 0.5 1.8828-0.20312 2.5859l-3.7227 3.7227c-0.73438 0.73438-1.8945 0.77734-2.6797 0.13281zm-9.0078-8.9961c-1.3281 0.76562-2.75 1.3594-4.2266 1.7734-1.3711 0.37891-2.7812 0.60547-4.2109 0.66406l-3.375 12.598 5.2109-5.2109c-0.18359-0.51562-0.27734-1.0547-0.27734-1.5938 0-1.1992 0.46094-2.4023 1.3789-3.3203 0.91406-0.91406 2.1211-1.375 3.3203-1.375 1.1953 0 2.3945 0.46094 3.3125 1.375 0.92187 0.92578 1.3828 2.1289 1.3828 3.3203 0 1.1406-0.41797 2.2852-1.25 3.1836-0.88672 0.96875-2.1328 1.5117-3.4453 1.5117-0.53906 0-1.0742-0.09375-1.5898-0.28125l-5.2148 5.2148 12.598-3.375c0.058594-1.4336 0.28516-2.8477 0.66406-4.2109 0.41406-1.4766 1.0078-2.8984 1.7734-4.2266l-6.0508-6.0508zm11.168 3.7227-8.8438-8.8438-0.89453 0.89453 8.8438 8.8438zm37.645-52.945 6.4609 6.4609 5.0742-5.0742-6.4609-6.4609zm-0.089844 13.012-6.4609-6.4609-35.871 35.871 6.4609 6.4609zm4.6367-2.8086-8.293-8.293c-0.12109-0.054687-0.23828-0.125-0.34766-0.20312l-0.82422 0.82422 8.8438 8.8438 0.82031-0.82031c-0.082031-0.10938-0.14844-0.23047-0.20312-0.35156zm-75.164 68.324c-0.83984-0.71094-0.94531-1.9727-0.23438-2.8125 0.71094-0.83984 1.9727-0.94531 2.8125-0.23438 11.77 10.004 24.934 4.5469 38.125-0.91797 14.867-6.1602 29.77-12.34 43.941 0.98828 0.80078 0.75391 0.83984 2.0195 0.085937 2.8203-0.75391 0.80078-2.0195 0.83984-2.8203 0.085937-12.289-11.559-26-5.875-39.676-0.20703-14.32 5.9336-28.609 11.855-42.234 0.27734zm19.625-20.375c0.23437 0 0.37891-0.09375 0.53906-0.24609 0.10156-0.125 0.15625-0.28516 0.15625-0.44922 0-0.18359-0.066406-0.36328-0.19922-0.49609-0.13281-0.13281-0.3125-0.19922-0.49609-0.19922-0.17969 0-0.35938 0.070312-0.49219 0.20312-0.13281 0.13281-0.20312 0.31641-0.20312 0.49219 0 0.17969 0.070313 0.35938 0.20312 0.49219 0.13281 0.13672 0.30078 0.20312 0.49219 0.20312z\" } }] }, { \"tagName\": \"clipPath\", \"attrs\": { \"width\": \"16\", \"height\": \"16\", \"viewBox\": \"0 0 16 16\", \"fill\": \"currentColor\", \"id\": \"icon-gojs-link\" }, \"children\": [{ \"tagName\": \"path\", \"attrs\": { \"d\": \"M 0.75 3.5 A 1.75 1.75 0 1 0 4.25 3.5 A 1.75 1.75 0 1 0 0.75 3.5 Z\" } }, { \"tagName\": \"path\", \"attrs\": { \"d\": \"M 4.25 2.75 L 9.5 2.75 L 9.5 11.75 L 13 11.75 L 13 13.25 L 8 13.25 L 8 4.25 L 4.25 4.25 Z\" } }, { \"tagName\": \"path\", \"attrs\": { \"d\": \"M 13 11 L 15.5 12.5 L 13 14 Z\" } }] }] }] };\nvar clipPaths_default = svgJson;\n\n// packages/playwright-core/src/utils/isomorphic/volatileDate.ts\nvar kMonthNamePattern = \"(?:jan(?:uary)?|feb(?:ruary)?|mar(?:ch)?|apr(?:il)?|may|jun(?:e)?|jul(?:y)?|aug(?:ust)?|sep(?:t(?:ember)?)?|oct(?:ober)?|nov(?:ember)?|dec(?:ember)?)\";\nvar kVolatileDateTokenRe = new RegExp([\n // Numeric date(-time) form. Apps default-name new entities with a creation\n // timestamp (\"Untitled 2026-07-12 17:30:03\"), and row names concatenate\n // further cells after it, so the numeric form also sits MID-name — where\n // the leading/trailing-only strips in hasStrippableDate never match.\n // Deliberately NO leading word boundary: elementText glues adjacent cell\n // texts without a separator (\"data.csv2026/05/18 23:59\"), so the year can\n // start at a letter-digit seam. The required [-/] separators keep\n // version-like number runs (\"release 1.2.3\") from being flagged.\n \"\\\\d{4}[-/]\\\\d{1,2}[-/]\\\\d{1,2}(?:[\\\\sT,]\\\\d{1,2}:\\\\d{1,2}(?::\\\\d{1,2})?)?\",\n `\\\\b${kMonthNamePattern}\\\\b\\\\.?\\\\s+\\\\d{1,2}(?:,?\\\\s*\\\\d{4})?\\\\b`,\n // Day-month form (\"10 Apr\", \"10 Apr 2025\"). The (?!\\s*\\d) guard rejects a\n // false parse where a stable name's trailing digit is read as the day —\n // in \"Marketing 4 Apr 19, 2024\" the \"4 Apr\" is followed by the real day\n // number, so the month-day branch above matches \"Apr 19, 2024\" instead.\n `\\\\b\\\\d{1,2}\\\\s+${kMonthNamePattern}\\\\b\\\\.?(?:\\\\s+\\\\d{4}\\\\b)?(?!\\\\s*\\\\d)`,\n // The (?!['’]) guard keeps possessive/stable labels (\"Today's Deals\",\n // \"Tomorrow's Agenda\") from being flagged — the apostrophe continuation\n // means the word is part of a larger noun phrase, not a date cell.\n `\\\\b(?:today|yesterday|tomorrow|just now)\\\\b(?!['\\u2019])`,\n // \"a few\" covers moment.js/dayjs's default smallest bucket (\"a few seconds ago\").\n \"\\\\b(?:a few|an?|\\\\d+)\\\\s+(?:second|minute|hour|day|week|month|year)s?\\\\s+ago\\\\b\"\n].join(\"|\"), \"i\");\nfunction hasVolatileDateFragment(text) {\n return kVolatileDateTokenRe.test(text);\n}\nvar kVolatileDateTokenStickyRe = new RegExp(kVolatileDateTokenRe.source, \"iy\");\n\n// packages/injected/src/domUtils.ts\nfunction parentElementOrShadowHost(element) {\n if (element.parentElement)\n return element.parentElement;\n if (!element.parentNode)\n return;\n if (element.parentNode.nodeType === 11 && element.parentNode.host)\n return element.parentNode.host;\n}\nfunction enclosingShadowRootOrDocument(element) {\n let node = element;\n while (node.parentNode)\n node = node.parentNode;\n if (node.nodeType === 11 || node.nodeType === 9)\n return node;\n}\nfunction enclosingShadowHost(element) {\n while (element.parentElement)\n element = element.parentElement;\n return parentElementOrShadowHost(element);\n}\nfunction closestCrossShadow(element, css, scope) {\n while (element) {\n const closest = element.closest(css);\n if (scope && closest !== scope && (closest == null ? void 0 : closest.contains(scope)))\n return;\n if (closest)\n return closest;\n element = enclosingShadowHost(element);\n }\n}\nfunction elementSafeTagName(element) {\n const tagName = element.tagName;\n if (typeof tagName === \"string\")\n return tagName.toUpperCase();\n if (element instanceof HTMLFormElement)\n return \"FORM\";\n return element.tagName.toUpperCase();\n}\n\n// packages/injected/src/roleUtils.ts\nfunction hasExplicitAccessibleName(e) {\n return e.hasAttribute(\"aria-label\") || e.hasAttribute(\"aria-labelledby\");\n}\nvar kAncestorPreventingLandmark = \"article:not([role]), aside:not([role]), main:not([role]), nav:not([role]), section:not([role]), [role=article], [role=complementary], [role=main], [role=navigation], [role=region]\";\nvar kGlobalAriaAttributes = [\n [\"aria-atomic\", void 0],\n [\"aria-busy\", void 0],\n [\"aria-controls\", void 0],\n [\"aria-current\", void 0],\n [\"aria-describedby\", void 0],\n [\"aria-details\", void 0],\n // Global use deprecated in ARIA 1.2\n // ['aria-disabled', undefined],\n [\"aria-dropeffect\", void 0],\n // Global use deprecated in ARIA 1.2\n // ['aria-errormessage', undefined],\n [\"aria-flowto\", void 0],\n [\"aria-grabbed\", void 0],\n // Global use deprecated in ARIA 1.2\n // ['aria-haspopup', undefined],\n [\"aria-hidden\", void 0],\n // Global use deprecated in ARIA 1.2\n // ['aria-invalid', undefined],\n [\"aria-keyshortcuts\", void 0],\n [\"aria-label\", [\"caption\", \"code\", \"deletion\", \"emphasis\", \"generic\", \"insertion\", \"paragraph\", \"presentation\", \"strong\", \"subscript\", \"superscript\"]],\n [\"aria-labelledby\", [\"caption\", \"code\", \"deletion\", \"emphasis\", \"generic\", \"insertion\", \"paragraph\", \"presentation\", \"strong\", \"subscript\", \"superscript\"]],\n [\"aria-live\", void 0],\n [\"aria-owns\", void 0],\n [\"aria-relevant\", void 0],\n [\"aria-roledescription\", [\"generic\"]]\n];\nfunction hasGlobalAriaAttribute(element, forRole) {\n return kGlobalAriaAttributes.some(([attr, prohibited]) => {\n return !(prohibited == null ? void 0 : prohibited.includes(forRole || \"\")) && element.hasAttribute(attr);\n });\n}\nfunction hasTabIndex(element) {\n return !Number.isNaN(Number(String(element.getAttribute(\"tabindex\"))));\n}\nfunction isFocusable(element) {\n return !isNativelyDisabled(element) && (isNativelyFocusable(element) || hasTabIndex(element));\n}\nfunction isNativelyFocusable(element) {\n const tagName = elementSafeTagName(element);\n if ([\"BUTTON\", \"DETAILS\", \"SELECT\", \"TEXTAREA\"].includes(tagName))\n return true;\n if (tagName === \"A\" || tagName === \"AREA\")\n return element.hasAttribute(\"href\");\n if (tagName === \"INPUT\")\n return !element.hidden;\n return false;\n}\nvar kImplicitRoleByTagName = {\n \"A\": (e) => {\n return e.hasAttribute(\"href\") ? \"link\" : null;\n },\n \"AREA\": (e) => {\n return e.hasAttribute(\"href\") ? \"link\" : null;\n },\n \"ARTICLE\": () => \"article\",\n \"ASIDE\": () => \"complementary\",\n \"BLOCKQUOTE\": () => \"blockquote\",\n \"BUTTON\": () => \"button\",\n \"CAPTION\": () => \"caption\",\n \"CODE\": () => \"code\",\n \"DATALIST\": () => \"listbox\",\n \"DD\": () => \"definition\",\n \"DEL\": () => \"deletion\",\n \"DETAILS\": () => \"group\",\n \"DFN\": () => \"term\",\n \"DIALOG\": () => \"dialog\",\n \"DT\": () => \"term\",\n \"EM\": () => \"emphasis\",\n \"FIELDSET\": () => \"group\",\n \"FIGURE\": () => \"figure\",\n \"FOOTER\": (e) => closestCrossShadow(e, kAncestorPreventingLandmark) ? null : \"contentinfo\",\n \"FORM\": (e) => hasExplicitAccessibleName(e) ? \"form\" : null,\n \"H1\": () => \"heading\",\n \"H2\": () => \"heading\",\n \"H3\": () => \"heading\",\n \"H4\": () => \"heading\",\n \"H5\": () => \"heading\",\n \"H6\": () => \"heading\",\n \"HEADER\": (e) => closestCrossShadow(e, kAncestorPreventingLandmark) ? null : \"banner\",\n \"HR\": () => \"separator\",\n \"HTML\": () => \"document\",\n \"IMG\": (e) => e.getAttribute(\"alt\") === \"\" && !e.getAttribute(\"title\") && !hasGlobalAriaAttribute(e) && !hasTabIndex(e) ? \"presentation\" : \"img\",\n \"INPUT\": (e) => {\n const type = e.type.toLowerCase();\n if (type === \"search\")\n return e.hasAttribute(\"list\") ? \"combobox\" : \"searchbox\";\n if ([\"email\", \"tel\", \"text\", \"url\", \"\"].includes(type)) {\n const list = getIdRefs(e, e.getAttribute(\"list\"))[0];\n return list && elementSafeTagName(list) === \"DATALIST\" ? \"combobox\" : \"textbox\";\n }\n if (type === \"hidden\")\n return null;\n if (type === \"file\")\n return \"button\";\n return inputTypeToRole[type] || \"textbox\";\n },\n \"INS\": () => \"insertion\",\n \"LI\": () => \"listitem\",\n \"MAIN\": () => \"main\",\n \"MARK\": () => \"mark\",\n \"MATH\": () => \"math\",\n \"MENU\": () => \"list\",\n \"METER\": () => \"meter\",\n \"NAV\": () => \"navigation\",\n \"OL\": () => \"list\",\n \"OPTGROUP\": () => \"group\",\n \"OPTION\": () => \"option\",\n \"OUTPUT\": () => \"status\",\n \"P\": () => \"paragraph\",\n \"PROGRESS\": () => \"progressbar\",\n \"SEARCH\": () => \"search\",\n \"SECTION\": (e) => hasExplicitAccessibleName(e) ? \"region\" : null,\n \"SELECT\": (e) => e.hasAttribute(\"multiple\") || e.size > 1 ? \"listbox\" : \"combobox\",\n \"STRONG\": () => \"strong\",\n \"SUB\": () => \"subscript\",\n \"SUP\": () => \"superscript\",\n // For <svg> we default to Chrome behavior:\n // - Chrome reports 'img'.\n // - Firefox reports 'diagram' that is not in official ARIA spec yet.\n // - Safari reports 'no role', but still computes accessible name.\n \"SVG\": () => \"img\",\n \"TABLE\": () => \"table\",\n \"TBODY\": () => \"rowgroup\",\n \"TD\": (e) => {\n const table = closestCrossShadow(e, \"table\");\n const role = table ? getExplicitAriaRole(table) : \"\";\n return role === \"grid\" || role === \"treegrid\" ? \"gridcell\" : \"cell\";\n },\n \"TEXTAREA\": () => \"textbox\",\n \"TFOOT\": () => \"rowgroup\",\n \"TH\": (e) => {\n const scope = e.getAttribute(\"scope\");\n if (scope === \"col\" || scope === \"colgroup\")\n return \"columnheader\";\n if (scope === \"row\" || scope === \"rowgroup\")\n return \"rowheader\";\n const nextSibling = e.nextElementSibling;\n const prevSibling = e.previousElementSibling;\n const row = !!e.parentElement && elementSafeTagName(e.parentElement) === \"TR\" ? e.parentElement : void 0;\n if (!nextSibling && !prevSibling) {\n if (row) {\n const table = closestCrossShadow(row, \"table\");\n if (table && table.rows.length <= 1)\n return null;\n }\n return \"columnheader\";\n }\n if (isHeaderCell(nextSibling) && isHeaderCell(prevSibling))\n return \"columnheader\";\n if (isNonEmptyDataCell(nextSibling) || isNonEmptyDataCell(prevSibling))\n return \"rowheader\";\n return \"columnheader\";\n },\n \"THEAD\": () => \"rowgroup\",\n \"TIME\": () => \"time\",\n \"TR\": () => \"row\",\n \"UL\": () => \"list\"\n};\nfunction isHeaderCell(element) {\n return !!element && elementSafeTagName(element) === \"TH\";\n}\nfunction isNonEmptyDataCell(element) {\n var _a;\n if (!element || elementSafeTagName(element) !== \"TD\")\n return false;\n return !!(((_a = element.textContent) == null ? void 0 : _a.trim()) || element.children.length > 0);\n}\nvar kPresentationInheritanceParents = {\n \"DD\": [\"DL\", \"DIV\"],\n \"DIV\": [\"DL\"],\n \"DT\": [\"DL\", \"DIV\"],\n \"LI\": [\"OL\", \"UL\"],\n \"TBODY\": [\"TABLE\"],\n \"TD\": [\"TR\"],\n \"TFOOT\": [\"TABLE\"],\n \"TH\": [\"TR\"],\n \"THEAD\": [\"TABLE\"],\n \"TR\": [\"THEAD\", \"TBODY\", \"TFOOT\", \"TABLE\"]\n};\nfunction getImplicitAriaRole(element) {\n var _a;\n const implicitRole = ((_a = kImplicitRoleByTagName[elementSafeTagName(element)]) == null ? void 0 : _a.call(kImplicitRoleByTagName, element)) || \"\";\n if (!implicitRole)\n return null;\n let ancestor = element;\n while (ancestor) {\n const parent = parentElementOrShadowHost(ancestor);\n const parents = kPresentationInheritanceParents[elementSafeTagName(ancestor)];\n if (!parents || !parent || !parents.includes(elementSafeTagName(parent)))\n break;\n const parentExplicitRole = getExplicitAriaRole(parent);\n if ((parentExplicitRole === \"none\" || parentExplicitRole === \"presentation\") && !hasPresentationConflictResolution(parent, parentExplicitRole))\n return parentExplicitRole;\n ancestor = parent;\n }\n return implicitRole;\n}\nvar validRoles = [\n \"alert\",\n \"alertdialog\",\n \"application\",\n \"article\",\n \"banner\",\n \"blockquote\",\n \"button\",\n \"caption\",\n \"cell\",\n \"checkbox\",\n \"code\",\n \"columnheader\",\n \"combobox\",\n \"complementary\",\n \"contentinfo\",\n \"definition\",\n \"deletion\",\n \"dialog\",\n \"directory\",\n \"document\",\n \"emphasis\",\n \"feed\",\n \"figure\",\n \"form\",\n \"generic\",\n \"grid\",\n \"gridcell\",\n \"group\",\n \"heading\",\n \"img\",\n \"insertion\",\n \"link\",\n \"list\",\n \"listbox\",\n \"listitem\",\n \"log\",\n \"main\",\n \"mark\",\n \"marquee\",\n \"math\",\n \"meter\",\n \"menu\",\n \"menubar\",\n \"menuitem\",\n \"menuitemcheckbox\",\n \"menuitemradio\",\n \"navigation\",\n \"none\",\n \"note\",\n \"option\",\n \"paragraph\",\n \"presentation\",\n \"progressbar\",\n \"radio\",\n \"radiogroup\",\n \"region\",\n \"row\",\n \"rowgroup\",\n \"rowheader\",\n \"scrollbar\",\n \"search\",\n \"searchbox\",\n \"separator\",\n \"slider\",\n \"spinbutton\",\n \"status\",\n \"strong\",\n \"subscript\",\n \"superscript\",\n \"switch\",\n \"tab\",\n \"table\",\n \"tablist\",\n \"tabpanel\",\n \"term\",\n \"textbox\",\n \"time\",\n \"timer\",\n \"toolbar\",\n \"tooltip\",\n \"tree\",\n \"treegrid\",\n \"treeitem\"\n];\nfunction getExplicitAriaRole(element) {\n const roles = (element.getAttribute(\"role\") || \"\").split(\" \").map((role) => role.trim());\n return roles.find((role) => validRoles.includes(role)) || null;\n}\nfunction hasPresentationConflictResolution(element, role) {\n return hasGlobalAriaAttribute(element, role) || isFocusable(element);\n}\nfunction getAriaRole(element) {\n const explicitRole = getExplicitAriaRole(element);\n if (!explicitRole)\n return getImplicitAriaRole(element);\n if (explicitRole === \"none\" || explicitRole === \"presentation\") {\n const implicitRole = getImplicitAriaRole(element);\n if (hasPresentationConflictResolution(element, implicitRole))\n return implicitRole;\n }\n return explicitRole;\n}\nfunction getIdRefs(element, ref) {\n if (!ref)\n return [];\n const root = enclosingShadowRootOrDocument(element);\n if (!root)\n return [];\n try {\n const ids = ref.split(\" \").filter((id) => !!id);\n const result = [];\n for (const id of ids) {\n const firstElement = root.querySelector(\"#\" + CSS.escape(id));\n if (firstElement && !result.includes(firstElement))\n result.push(firstElement);\n }\n return result;\n } catch (e) {\n return [];\n }\n}\nfunction isNativelyDisabled(element) {\n const isNativeFormControl = [\"BUTTON\", \"INPUT\", \"SELECT\", \"TEXTAREA\", \"OPTION\", \"OPTGROUP\"].includes(elementSafeTagName(element));\n return isNativeFormControl && (element.hasAttribute(\"disabled\") || belongsToDisabledOptGroup(element) || belongsToDisabledFieldSet(element));\n}\nfunction belongsToDisabledOptGroup(element) {\n return elementSafeTagName(element) === \"OPTION\" && !!element.closest(\"OPTGROUP[DISABLED]\");\n}\nfunction belongsToDisabledFieldSet(element) {\n const fieldSetElement = element == null ? void 0 : element.closest(\"FIELDSET[DISABLED]\");\n if (!fieldSetElement)\n return false;\n const legendElement = fieldSetElement.querySelector(\":scope > LEGEND\");\n return !legendElement || !legendElement.contains(element);\n}\nvar inputTypeToRole = {\n \"button\": \"button\",\n \"checkbox\": \"checkbox\",\n \"image\": \"button\",\n \"number\": \"spinbutton\",\n \"radio\": \"radio\",\n \"range\": \"slider\",\n \"reset\": \"button\",\n \"submit\": \"button\"\n};\n\n// packages/injected/src/recorder/skyramp/ScopingHandler.ts\nvar LOG_PREFIX = \"[Scoping]\";\nfunction isLogEnabled() {\n try {\n if (typeof window !== \"undefined\" && window.__SKYRAMP_DEBUG__) {\n return true;\n }\n if (typeof localStorage !== \"undefined\" && localStorage.getItem(\"SKYRAMP_DEBUG\") === \"true\") {\n return true;\n }\n } catch {\n }\n return false;\n}\nfunction log(...args) {\n if (isLogEnabled()) {\n console.log(LOG_PREFIX, ...args);\n }\n}\nfunction logGroup(label) {\n if (isLogEnabled()) {\n console.group(`${LOG_PREFIX} ${label}`);\n }\n}\nfunction logGroupEnd() {\n if (isLogEnabled()) {\n console.groupEnd();\n }\n}\nfunction logTable(data) {\n if (isLogEnabled()) {\n console.table(data);\n }\n}\nvar CSS_ITEM_PATTERNS = [\n /card/i,\n // product-card, hot-product-card, card-item\n /item/i,\n // list-item, grid-item, menu-item\n /tile/i,\n // product-tile, image-tile\n /cell/i,\n // grid-cell, table-cell\n /row(?!s)/i,\n // data-row, table-row (but not \"rows\")\n /entry/i,\n // feed-entry, log-entry\n /result/i,\n // search-result, result-item\n /post/i,\n // blog-post, feed-post\n /product/i,\n // product, product-listing\n /option/i\n // select-option, dropdown-option (Sentry uses [role=\"option\"])\n];\nvar CSS_SKIP_PATTERNS = [\n /^col-/i,\n // Bootstrap columns: col-md-4, col-12\n /^row$/i,\n // Bootstrap row (exact match)\n /^container/i,\n // container, container-fluid\n /^px-/i,\n /^py-/i,\n // Padding utilities\n /^mx-/i,\n /^my-/i,\n // Margin utilities\n /^m-/i,\n /^p-/i,\n // Single margin/padding\n /^d-/i,\n // Display utilities: d-flex, d-none\n /^flex/i,\n // Flexbox utilities\n /^grid$/i,\n // Grid utility\n /^text-/i,\n // Text utilities\n /^bg-/i,\n // Background utilities\n /^border/i,\n // Border utilities\n /^rounded/i,\n // Border radius utilities\n /^shadow/i,\n // Shadow utilities\n /^w-/i,\n /^h-/i,\n // Width/height utilities\n /^css-/i,\n // CSS-in-JS: css-xxxxx\n /^styled-/i,\n // Styled-components\n /^sc-/i,\n // Styled-components\n /^emotion-/i,\n // Emotion CSS-in-JS\n /^MuiGrid/i,\n // Material-UI grid\n /^MuiBox/i,\n // Material-UI box\n /--[a-f0-9]{16,}$/i,\n // CSS-in-JS hash suffix: class--d5fc23da2c7ac21a\n /__[a-f0-9]{16,}$/i,\n // CSS-in-JS hash suffix: class__d5fc23da2c7ac21a\n /_[a-f0-9]{16,}$/i,\n // CSS-in-JS hash suffix: class_d5fc23da2c7ac21a\n // Sentry/Emotion short-form CSS-in-JS patterns\n /^app-[a-z0-9]+$/i,\n // Emotion: app-r5ldb0\n /^e[a-z0-9]{6,}\\d+$/i\n // Emotion: e1s9zdwb0, ebcy13q0\n];\nvar ScopingHandler = class {\n constructor(injectedScript) {\n this._injectedScript = injectedScript;\n log(\"ScopingHandler initialized (with CSS class pattern support)\");\n }\n // ==========================================================================\n // Main Hook Entry Point\n // ==========================================================================\n /**\n * Main hook - generates scoped selector using .nth() pattern:\n * container >> nth=N >> relativeSelector\n */\n applyScopingHook(element, selector, elements) {\n logGroup(`Analyzing: ${selector}`);\n const needsScoping = this._needsScoping(element, selector, elements);\n log(\"Needs scoping:\", needsScoping.needed, \"| Reason:\", needsScoping.reason);\n if (!needsScoping.needed) {\n const stableId2 = this._tryStableIdSelector(element, selector);\n if (stableId2) {\n log(\"Preferring stable ID selector:\", stableId2.selector);\n logGroupEnd();\n return stableId2;\n }\n logGroupEnd();\n return null;\n }\n const stableId = this._tryStableIdSelector(element, selector);\n if (stableId) {\n log(\"Using stable ID selector:\", stableId.selector);\n logGroupEnd();\n return stableId;\n }\n const linkSelector = this._tryLinkSelector(element, selector);\n if (linkSelector) {\n log(\"Using link selector:\", linkSelector.selector);\n logGroupEnd();\n return linkSelector;\n }\n const container = this._findContainer(element);\n if (!container) {\n log(\"No container found\");\n if (this._hasDynamicSelector(selector)) {\n const alternative = this._generateAlternativeSelector(element, selector);\n if (alternative) {\n log(\"Using alternative selector:\", alternative.selector);\n logGroupEnd();\n return alternative;\n }\n }\n logGroupEnd();\n return null;\n }\n log(\"Container:\", container.selector);\n const containerIndex = this._getContainerIndex(container.element, container.selector);\n if (containerIndex === null) {\n log(\"Cannot determine container index\");\n logGroupEnd();\n return null;\n }\n log(\"Container index:\", containerIndex);\n const relativeSelector = this._generateRelativeSelector(container.element, element);\n if (!relativeSelector) {\n log(\"No relative selector found\");\n logGroupEnd();\n return null;\n }\n log(\"Relative selector:\", relativeSelector);\n const isFormContainer = this._isFormContainer(container.selector);\n let scopedSelector;\n let usesTextFilter = false;\n if (isFormContainer) {\n scopedSelector = `${container.selector} >> ${relativeSelector}`;\n log(\"Using form container selector (no nth):\", scopedSelector);\n } else {\n const hasFilter = this._getRowHasFilter(container.element, container.selector);\n if (hasFilter) {\n scopedSelector = `${container.selector} >> internal:has=${hasFilter} >> ${relativeSelector}`;\n usesTextFilter = true;\n log(\"Using has-filter selector:\", scopedSelector);\n } else {\n const textFilter = this._getTextFilterForContainer(container.element, container.selector);\n if (textFilter) {\n scopedSelector = `${container.selector} >> internal:has-text=\"${this._escapeTextFilter(textFilter)}\"i >> ${relativeSelector}`;\n usesTextFilter = true;\n log(\"Using text-filtered selector:\", scopedSelector);\n } else {\n const rowAnchor = this._getRowAnchorSelector(container.element, element);\n if (rowAnchor) {\n scopedSelector = rowAnchor;\n usesTextFilter = true;\n log(\"Using row-anchored selector:\", scopedSelector);\n } else {\n scopedSelector = `${container.selector} >> nth=${containerIndex} >> ${relativeSelector}`;\n log(\"Using nth-based selector:\", scopedSelector);\n }\n }\n }\n }\n const verification = this._verifySelector(scopedSelector);\n log(\"Verification:\", verification.valid ? \"PASS\" : \"FAIL\", \"| Matches:\", verification.count);\n if (!verification.valid) {\n if (isFormContainer && verification.count >= 1) {\n log(\"Form container verification relaxed - using selector despite multiple matches\");\n } else {\n log(\"Verification failed, using original\");\n logGroupEnd();\n return null;\n }\n }\n const result = {\n selector: scopedSelector,\n container: container.element,\n elements: verification.elements,\n strategy: \"nth\",\n description: isFormContainer ? `${container.selector} >> ${relativeSelector}` : `${container.selector}.nth(${containerIndex}) >> ${relativeSelector}`,\n containerSelector: container.selector,\n containerIndex,\n relativeSelector,\n isFormContainer,\n usesTextFilter\n };\n logTable({\n \"Original\": selector,\n \"Scoped\": scopedSelector,\n \"Container\": container.selector,\n \"Index\": containerIndex,\n \"Relative\": relativeSelector\n });\n logGroupEnd();\n return result;\n }\n // ==========================================================================\n // Step 1: Check if Scoping Needed\n // ==========================================================================\n _needsScoping(element, selector, elements) {\n log(\"Input match set:\", { selector, count: elements.length });\n if (elements.length > 1) {\n return { needed: true, reason: `Non-unique: ${elements.length} elements` };\n }\n if (this._hasDynamicSelector(selector)) {\n return { needed: true, reason: \"Dynamic selector needs replacement\" };\n }\n const stateCheck = this._hasStateDependentName(element, selector);\n if (stateCheck.isStateDependent) {\n return { needed: true, reason: stateCheck.reason };\n }\n const repeatingRoles = [\"gridcell\", \"row\", \"listitem\", \"option\", \"treeitem\", \"menuitem\", \"cell\"];\n const elementRole = element.getAttribute(\"role\");\n if (elementRole && repeatingRoles.includes(elementRole)) {\n const container = this._findRepeatingContainer(element);\n if (container) {\n return { needed: true, reason: `Repeating role \"${elementRole}\" inside container: ${container.selector}` };\n }\n }\n const closestTd = element.tagName === \"TD\" ? element : element.closest(\"td\");\n if (closestTd) {\n const tr = closestTd.closest(\"tr\");\n if (tr) {\n const tbody = tr.parentElement;\n if (tbody && (tbody.tagName === \"TBODY\" || tbody.tagName === \"TABLE\")) {\n const rows = tbody.querySelectorAll(\":scope > tr\");\n if (rows.length > 1) {\n return { needed: true, reason: `Element inside <td> in <tr> with ${rows.length} sibling rows` };\n }\n }\n }\n }\n return { needed: false, reason: \"Selector is unique and not in repeating context\" };\n }\n // ==========================================================================\n // Stable ID Preference\n // ==========================================================================\n /**\n * Check if an element ID looks dynamic (generated at runtime).\n * Dynamic IDs change across sessions/page loads so they make fragile selectors.\n *\n * NOTE: The canonical source of truth for these rules is\n * `packages/playwright/src/dom-analyzer/dynamicId.ts` (exported as\n * `isDynamicId`). This local copy exists because cross-package imports\n * from injected to playwright are not used elsewhere in the repo and\n * add resolution risk. When adding or adjusting a rule here, update\n * the shared module in lockstep; drift will silently degrade blueprint\n * collision-resolution output quality (Bug 1.3).\n */\n _isDynamicId(id) {\n if (/^react-aria\\d+/.test(id)) return true;\n if (/^mui-\\d+/.test(id)) return true;\n if (/^(mat|cdk)-[a-z]+-\\d+$/.test(id)) return true;\n if (/[-_]\\d+$/.test(id)) return true;\n if (/\\d{2,}[_-][a-zA-Z]/.test(id)) return true;\n if (/^\\d+$/.test(id)) return true;\n if (/\\d{4,}$/.test(id)) return true;\n if (/[-_][0-9a-f]{6,}$/i.test(id)) return true;\n const shortHexMatch = id.match(/[-_]([0-9a-f]{3,5})$/i);\n if (shortHexMatch && /[0-9]/.test(shortHexMatch[1])) return true;\n if (/[a-zA-Z][0-9]{3,}$/.test(id)) return true;\n if (id.includes(\":\")) return true;\n if (/^.+__search_[a-zA-Z0-9]{4,}$/.test(id)) return true;\n return false;\n }\n /**\n * When the element has a stable (non-dynamic) ID, prefer #id over a role\n * selector. Role selectors can become ambiguous when page state differs\n * between recording and playback.\n * Only replaces role-based selectors — if the original is already ID-based\n * or testid-based, leave it alone.\n */\n _tryStableIdSelector(element, selector) {\n const id = element.id;\n if (!id) return null;\n if (!selector.startsWith(\"internal:role=\")) return null;\n if (this._isDynamicId(id)) {\n log(\"Skipping dynamic ID:\", id);\n return null;\n }\n const idSelector = `#${CSS.escape(id)}`;\n const verification = this._verifySelector(idSelector);\n if (!verification.valid || verification.count !== 1) {\n log(\"ID selector not unique:\", idSelector, \"matches:\", verification.count);\n return null;\n }\n return {\n selector: idSelector,\n container: null,\n elements: verification.elements,\n strategy: \"alternative\",\n description: `Stable ID preferred over role selector`,\n containerSelector: \"\",\n containerIndex: -1,\n relativeSelector: \"\",\n isAlternativeSelector: true\n };\n }\n /**\n * Check if selector contains dynamic/fragile patterns that should be replaced\n * Examples: #contextmenutarget19, #item-42, #row_123, internal:attr=[id=\"a-text-input_18\"]\n * Sentry: #react-aria9765209213-_r_nj_\n */\n _hasDynamicSelector(selector) {\n log(\"_hasDynamicSelector checking:\", selector);\n if (/react-aria\\d+/.test(selector)) {\n log(\"MATCHED: React-Aria dynamic ID\");\n return true;\n }\n if (/__search_[a-zA-Z0-9]{4,}/.test(selector)) {\n log(\"MATCHED: Vue Tables dynamic search ID\");\n return true;\n }\n if (/#[a-zA-Z_-]*\\d+/.test(selector)) {\n log(\"MATCHED: Dynamic ID with numeric suffix (CSS)\");\n return true;\n }\n if (/internal:attr=\\[id=\"[a-zA-Z_-]*\\d+\"\\]/.test(selector)) {\n log(\"MATCHED: Dynamic ID with numeric suffix (internal:attr)\");\n return true;\n }\n if (/\\[id=\"[a-zA-Z_-]*\\d+\"\\]/.test(selector)) {\n log(\"MATCHED: Dynamic ID with numeric suffix (attribute selector)\");\n return true;\n }\n if (/\\[id=\"[^\"]*\\d{2,}[_-][a-zA-Z][^\"]*\"\\]/.test(selector)) {\n log(\"MATCHED: Dynamic ID with mid-string counter (attribute selector)\");\n return true;\n }\n if (/internal:attr=\\[id=\"[^\"]*\\d{2,}[_-][a-zA-Z][^\"]*\"\\]/.test(selector)) {\n log(\"MATCHED: Dynamic ID with mid-string counter (internal:attr)\");\n return true;\n }\n if (/\\.(app-[a-z0-9]+|e[a-z0-9]{6,}[0-9]+)/.test(selector)) {\n log(\"MATCHED: CSS-in-JS generated class (Emotion)\");\n return true;\n }\n const childCombinatorCount = (selector.match(/>/g) || []).length;\n if (childCombinatorCount >= 4) {\n log(\"MATCHED: Long CSS path with\", childCombinatorCount, \"child combinators\");\n return true;\n }\n if (/:nth-child\\(\\d+\\)/.test(selector)) {\n log(\"MATCHED: Contains :nth-child() pattern\");\n return true;\n }\n if (/\\[data-testid=\"[^\"]*[-_]\\d+\"\\]/.test(selector) || /\\[data-test-id=\"[^\"]*[-_]\\d+\"\\]/.test(selector)) {\n log(\"MATCHED: TestId with numeric suffix (attribute selector)\");\n return true;\n }\n if (/getByTestId\\(['\"][^'\"]*[-_]\\d+['\"]\\)/.test(selector)) {\n log(\"MATCHED: TestId with numeric suffix (getByTestId)\");\n return true;\n }\n if (/internal:testid=.*[-_]\\d+/.test(selector)) {\n log(\"MATCHED: TestId with numeric suffix (internal:testid)\");\n return true;\n }\n if (/\\[name=\"\\d+\"[is]?\\]/.test(selector) || /internal:text=\"\\d+\"[is]?/.test(selector)) {\n log(\"MATCHED: All-numeric text content in selector\");\n return true;\n }\n if (/\\[name=\"[^\"]*\\d{8,}[^\"]*\"/.test(selector) || /internal:text=\"[^\"]*\\d{8,}[^\"]*\"/.test(selector)) {\n log(\"MATCHED: Long digit sequence in text content (timestamp/generated)\");\n return true;\n }\n if (/(?:\\[name|internal:text)=\"\\/?\\d{1,2}\\/\\d{1,2}\"/.test(selector)) {\n log(\"MATCHED: Partial date pattern in text content\");\n return true;\n }\n const textFragments = [...selector.matchAll(/(?:\\[name|internal:text)=\"([^\"]*)\"/g)].map((m) => m[1]);\n if (textFragments.some((t) => this._hasVolatileText(t))) {\n log(\"MATCHED: Month-name date in text content (volatile)\");\n return true;\n }\n log(\"No dynamic patterns found\");\n return false;\n }\n /**\n * Check if element's accessible name might be state-dependent (hover, focus, etc.)\n * This detects cases like Box.com where hovering shows a checkbox that changes\n * the accessible name from \"Personal Folder\" to \"Select Personal Folder\".\n *\n * Strategy: Check if our element's accessible name CONTAINS a substring that\n * matches siblings' accessible names. This indicates the name might be\n * augmented by hover state.\n *\n * Example (Box.com - needs scoping):\n * - Element name: \"Select Personal Folder\" (unique at recording, but hover-dependent)\n * - Sibling names: \"Personal Folder\", \"Personal Folder\", \"Personal Folder\"\n * - \"Select Personal Folder\" contains \"Personal Folder\" → state-dependent\n *\n * Counter-example (Knode.ai - should NOT be scoped):\n * - Element name: \"Teams\"\n * - Sibling names: \"Dashboard\", \"Calls\", \"Users\", \"Integrations\"\n * - \"Teams\" doesn't contain any sibling name → NOT state-dependent\n */\n _hasStateDependentName(element, selector) {\n const nameMatch = selector.match(/internal:role=(\\w+)\\[name=[\"'](.+?)[\"'][is]?\\]/);\n if (!nameMatch) {\n return { isStateDependent: false, reason: \"Not a role selector with name\" };\n }\n const role = nameMatch[1];\n const elementName = nameMatch[2];\n if (!elementName || elementName.length < 3) {\n return { isStateDependent: false, reason: \"Name too short\" };\n }\n log(\"Checking state-dependent name:\", { role, name: elementName });\n const siblingNames = this._getSiblingAccessibleNames(element, role);\n if (siblingNames.length === 0) {\n log(\"No siblings found for state check\");\n return { isStateDependent: false, reason: \"No siblings with same role\" };\n }\n log(\"Sibling names:\", siblingNames);\n for (const siblingName of siblingNames) {\n if (siblingName.length >= 3 && elementName.length > siblingName.length) {\n if (elementName.toLowerCase().includes(siblingName.toLowerCase())) {\n log(\"State-dependent name detected:\", elementName, \"contains\", siblingName);\n return {\n isStateDependent: true,\n reason: `Name \"${elementName}\" contains sibling name \"${siblingName}\" - likely hover-dependent`\n };\n }\n }\n }\n return { isStateDependent: false, reason: \"Name is unique among siblings\" };\n }\n /**\n * Get accessible names of sibling elements with the same role\n * Only looks within the same parent container (not the entire document)\n * to avoid false positives from unrelated elements elsewhere on the page.\n * Excludes the target element and elements with duplicate names.\n */\n _getSiblingAccessibleNames(element, role) {\n const names = [];\n const seen = /* @__PURE__ */ new Set();\n try {\n const containerSelectors = [\"ul\", \"ol\", \"nav\", '[role=\"list\"]', '[role=\"navigation\"]', '[role=\"menu\"]', '[role=\"tablist\"]', '[role=\"grid\"]', '[role=\"row\"]'];\n let container = element.parentElement;\n let searchScope = element.ownerDocument.body;\n let depth = 0;\n while (container && depth < 5) {\n const tagLower = container.tagName.toLowerCase();\n const containerRole = container.getAttribute(\"role\");\n if (containerSelectors.some((sel) => {\n var _a;\n if (sel.startsWith(\"[role=\")) {\n const roleVal = (_a = sel.match(/\\[role=\"(.+)\"\\]/)) == null ? void 0 : _a[1];\n return containerRole === roleVal;\n }\n return tagLower === sel;\n })) {\n searchScope = container;\n break;\n }\n container = container.parentElement;\n depth++;\n }\n log(\"Sibling search scope:\", searchScope.tagName, searchScope.className);\n const selector = `[role=\"${role}\"]`;\n const siblings = searchScope.querySelectorAll(selector);\n for (const sibling of siblings) {\n if (sibling === element) continue;\n const name = this._getAccessibleName(sibling);\n if (name && name.length >= 2 && !seen.has(name.toLowerCase())) {\n seen.add(name.toLowerCase());\n names.push(name);\n }\n }\n } catch (e) {\n log(\"Error getting sibling names:\", e);\n }\n return names;\n }\n /**\n * Check if element is inside a repeating container (multiple siblings with same testid/role/class)\n */\n _findRepeatingContainer(element) {\n const itemPatterns = [\n /^grid[-_]?view[-_]?item$/i,\n /^gridcell$/i,\n /^list[-_]?item$/i,\n /^row[-_]?item$/i,\n /^item$/i,\n /^card$/i,\n /^tile$/i\n ];\n const skipPatterns = [\n /^gridview$/i,\n /^grid[-_]?view$/i,\n /^listview$/i,\n /^list[-_]?view$/i,\n /^container$/i,\n /^wrapper$/i,\n /^content$/i,\n /^main$/i\n ];\n let current = element.parentElement;\n while (current && current !== element.ownerDocument.body) {\n const testId = this._getTestId(current);\n if (testId && !skipPatterns.some((p) => p.test(testId))) {\n if (itemPatterns.some((p) => p.test(testId))) {\n const selector = this._buildTestIdSelector(current, testId);\n const siblings = current.ownerDocument.querySelectorAll(selector);\n if (siblings.length > 1) {\n return { element: current, selector };\n }\n }\n }\n const role = current.getAttribute(\"role\");\n if (role && [\"row\", \"gridcell\", \"listitem\", \"option\", \"treeitem\", \"menuitem\"].includes(role)) {\n const selector = `[role=${this._quoteCSSAttributeValue(role)}]`;\n const siblings = current.ownerDocument.querySelectorAll(selector);\n if (siblings.length > 1) {\n return { element: current, selector };\n }\n }\n const componentInfo = this._getComponentName(current);\n if (componentInfo && this._isRepeatingComponentName(componentInfo.name)) {\n const selector = `[${componentInfo.attr}=${this._quoteCSSAttributeValue(componentInfo.name)}]`;\n try {\n const siblings = current.ownerDocument.querySelectorAll(selector);\n if (siblings.length > 1 && siblings.length < 100) {\n log(\"Found component container:\", componentInfo.name, \"via\", componentInfo.attr, \"with\", siblings.length, \"siblings\");\n return { element: current, selector };\n }\n } catch {\n }\n }\n const cssContainer = this._findRepeatingContainerByClass(current);\n if (cssContainer) {\n return cssContainer;\n }\n current = current.parentElement;\n }\n return null;\n }\n /**\n * Check if element has CSS classes that indicate a repeating container\n * Returns the container info if found, null otherwise\n *\n * IMPORTANT: Uses TAG NAME as selector instead of CSS class for stability.\n * CSS classes are only used to DETECT repeating containers, but the\n * selector uses the stable tag name. Text filtering handles uniqueness.\n */\n _findRepeatingContainerByClass(element) {\n const classList = Array.from(element.classList);\n for (const cls of classList) {\n if (CSS_SKIP_PATTERNS.some((p) => p.test(cls))) {\n continue;\n }\n if (CSS_ITEM_PATTERNS.some((p) => p.test(cls))) {\n const cssSelector = `.${this._escapeCSS(cls)}`;\n try {\n const siblings = element.ownerDocument.querySelectorAll(cssSelector);\n if (siblings.length > 1 && siblings.length < 100) {\n const tagSelector = element.tagName.toLowerCase();\n log(\"Found CSS class container:\", cls, \"with\", siblings.length, \"siblings, using tag:\", tagSelector);\n return { element, selector: tagSelector };\n }\n } catch {\n continue;\n }\n }\n }\n return null;\n }\n /**\n * Escape CSS class name for use in selector\n * Handles special characters that need escaping\n */\n _escapeCSS(value) {\n return escapeCSS(value);\n }\n /**\n * Quote and escape a value for use in CSS attribute selectors\n * Escapes backslashes and double quotes to prevent malformed selectors\n * Example: value with \"quotes\" -> \"value with \\\"quotes\\\"\"\n */\n _quoteCSSAttributeValue(text) {\n return quoteCSSAttributeValue(text);\n }\n /**\n * Get test ID from element - supports both data-testid and data-test-id (Sentry uses hyphen)\n */\n _getTestId(element) {\n return element.getAttribute(\"data-testid\") || element.getAttribute(\"data-test-id\");\n }\n /**\n * Build a selector for test ID - uses whichever attribute the element has\n */\n _buildTestIdSelector(element, testId) {\n if (element.getAttribute(\"data-testid\") === testId) {\n return `[data-testid=${this._quoteCSSAttributeValue(testId)}]`;\n }\n return `[data-test-id=${this._quoteCSSAttributeValue(testId)}]`;\n }\n /**\n * Get component name from element using common data-* attributes\n * Supports: data-component, data-sentry-component, data-react-component\n */\n _getComponentName(element) {\n const componentAttrs = [\"data-component\", \"data-sentry-component\", \"data-react-component\"];\n for (const attr of componentAttrs) {\n const value = element.getAttribute(attr);\n if (value) {\n return { name: value, attr };\n }\n }\n return null;\n }\n /**\n * Check if component name indicates a repeating container\n * Based on common naming conventions (Card, Item, Row, etc.)\n */\n _isRepeatingComponentName(componentName) {\n const repeatingPatterns = [\n /Card$/i,\n // DashboardCard, ProductCard\n /Item$/i,\n // ListItem, GridItem\n /Link$/i,\n // NavLink (when in lists)\n /Row$/i,\n // TableRow, DataRow\n /Tile$/i,\n // GridTile, ImageTile\n /Option$/i,\n // SelectOption, DropdownOption\n /Entry$/i\n // FeedEntry, LogEntry\n ];\n return repeatingPatterns.some((p) => p.test(componentName));\n }\n // ==========================================================================\n // Step 2: Find Container\n // ==========================================================================\n _findContainer(element) {\n const itemPatterns = [\n /^grid[-_]?view[-_]?item$/i,\n /^gridcell$/i,\n /^list[-_]?item$/i,\n /^row[-_]?item$/i,\n /^item$/i,\n /^card$/i,\n /^tile$/i\n ];\n const formContainerPatterns = [\n /[-_]input$/i,\n // edit-name-input, search-input\n /[-_]field$/i,\n // name-field, email-field\n /[-_]btn$/i,\n // edit-btn, submit-btn\n /[-_]button$/i,\n // save-button, cancel-button\n /[-_]control$/i,\n // date-control, select-control\n /^input[-_]/i,\n // input-name, input-email\n /^field[-_]/i\n // field-name, field-email\n ];\n const skipPatterns = [\n /^gridview$/i,\n /^grid[-_]?view$/i,\n /^listview$/i,\n /^list[-_]?view$/i,\n /^container$/i,\n /^wrapper$/i,\n /^content$/i,\n /^main$/i\n ];\n let current = element.parentElement;\n let candidate = null;\n let cssCandidate = null;\n while (current && current !== element.ownerDocument.body) {\n const testId = this._getTestId(current);\n if (testId) {\n if (skipPatterns.some((p) => p.test(testId))) {\n current = current.parentElement;\n continue;\n }\n const dynamicTestId = this._isFragileTestId(testId);\n if (!dynamicTestId && itemPatterns.some((p) => p.test(testId))) {\n return { element: current, selector: this._buildTestIdSelector(current, testId) };\n }\n if (!dynamicTestId && formContainerPatterns.some((p) => p.test(testId))) {\n log(\"Found form container by testid pattern:\", testId);\n return { element: current, selector: this._buildTestIdSelector(current, testId) };\n }\n if (!candidate && !dynamicTestId) {\n candidate = { element: current, selector: this._buildTestIdSelector(current, testId) };\n }\n }\n const role = current.getAttribute(\"role\");\n if (role && [\"row\", \"gridcell\", \"listitem\", \"option\", \"treeitem\", \"menuitem\"].includes(role)) {\n if (!candidate) {\n candidate = { element: current, selector: `[role=${this._quoteCSSAttributeValue(role)}]` };\n }\n }\n if (current.tagName === \"TR\") {\n const tbody = current.parentElement;\n if (tbody && (tbody.tagName === \"TBODY\" || tbody.tagName === \"TABLE\")) {\n const rows = tbody.querySelectorAll(\":scope > tr\");\n if (rows.length > 1 && !candidate) {\n candidate = { element: current, selector: \"tr\" };\n }\n }\n }\n const componentInfo = this._getComponentName(current);\n if (componentInfo && this._isRepeatingComponentName(componentInfo.name)) {\n const selector = `[${componentInfo.attr}=${this._quoteCSSAttributeValue(componentInfo.name)}]`;\n try {\n const siblings = current.ownerDocument.querySelectorAll(selector);\n if (siblings.length > 1 && siblings.length < 100) {\n log(\"Found component container:\", componentInfo.name, \"via\", componentInfo.attr, \"with\", siblings.length, \"siblings\");\n return { element: current, selector };\n }\n } catch {\n }\n }\n if (!cssCandidate) {\n const cssContainer = this._findRepeatingContainerByClass(current);\n if (cssContainer) {\n cssCandidate = cssContainer;\n }\n }\n current = current.parentElement;\n }\n return candidate || cssCandidate;\n }\n // ==========================================================================\n // Check if Container is a Form Container (unique, not repeating)\n // ==========================================================================\n /**\n * Check if the container selector matches form container patterns.\n * Form containers are unique wrappers for form elements (inputs, buttons, etc.)\n * They don't need nth() indexing because they're not repeating elements.\n */\n _isFormContainer(containerSelector) {\n const formContainerPatterns = [\n /-input\"\\]$/i,\n // [data-testid=\"edit-name-input\"]\n /-field\"\\]$/i,\n // [data-testid=\"name-field\"]\n /-btn\"\\]$/i,\n // [data-testid=\"edit-btn\"]\n /-button\"\\]$/i,\n // [data-testid=\"save-button\"]\n /-control\"\\]$/i,\n // [data-testid=\"date-control\"]\n /\\[data-test-?id=\"input-/i,\n // [data-testid=\"input-name\"] or [data-test-id=\"input-name\"]\n /\\[data-test-?id=\"field-/i\n // [data-testid=\"field-name\"] or [data-test-id=\"field-name\"]\n ];\n const isForm = formContainerPatterns.some((pattern) => pattern.test(containerSelector));\n log(\"_isFormContainer:\", containerSelector, \"=\", isForm);\n return isForm;\n }\n // ==========================================================================\n // Step 3: Get Container Index\n // ==========================================================================\n _getContainerIndex(container, containerSelector) {\n try {\n const all = container.ownerDocument.querySelectorAll(containerSelector);\n const idx = Array.from(all).indexOf(container);\n return idx !== -1 ? idx : null;\n } catch {\n return null;\n }\n }\n // ==========================================================================\n // Text-Based Container Filtering (for row-like containers)\n // ==========================================================================\n /**\n * Get unique identifying text for a container to use in has-text filter\n * Works with ANY repeating container type (rows, options, list items, etc.)\n * Returns null if no unique text can be found (fall back to nth)\n */\n _getTextFilterForContainer(container, containerSelector) {\n var _a;\n const role = container.getAttribute(\"role\");\n let identifyingText = null;\n if (role === \"row\" || container.tagName === \"TR\" || container.classList.contains(\"a-data-table__row\")) {\n identifyingText = this._getRowIdentifyingText(container);\n } else {\n identifyingText = this._getContainerIdentifyingText(container);\n }\n if (!identifyingText) {\n log(\"Text filter: No identifying text found\");\n return null;\n }\n const allContainers = container.ownerDocument.querySelectorAll(containerSelector);\n let matchCount = 0;\n for (const c of allContainers) {\n if ((_a = c.textContent) == null ? void 0 : _a.includes(identifyingText)) {\n matchCount++;\n }\n }\n if (matchCount === 1) {\n log(\"Text filter: Found unique text:\", identifyingText);\n return identifyingText;\n }\n log(\"Text filter: Text not unique, found in\", matchCount, \"containers\");\n return null;\n }\n /**\n * Extract identifying text from any container type (not just rows)\n * Used for [role=\"option\"], [role=\"listitem\"], etc.\n * Searches multiple levels deep for meaningful text\n */\n _getContainerIdentifyingText(container) {\n var _a;\n const directText = this._getDirectTextContent(container);\n if (directText && directText.length >= 2 && directText.length <= 100 && !this._isGenericText(directText)) {\n return directText;\n }\n const ariaLabel = container.getAttribute(\"aria-label\");\n if (ariaLabel && ariaLabel.length >= 2 && ariaLabel.length <= 100 && !this._isGenericText(ariaLabel)) {\n return ariaLabel;\n }\n const textElements = container.querySelectorAll(\"span, div, p, h1, h2, h3, h4, h5, h6\");\n for (const el of textElements) {\n const text = this._getDirectTextContent(el);\n if (text && text.length >= 2 && text.length <= 100 && !this._isGenericText(text)) {\n return text;\n }\n }\n const labeledElement = container.querySelector(\"[aria-label]\");\n if (labeledElement) {\n const label = labeledElement.getAttribute(\"aria-label\");\n if (label && label.length >= 2 && label.length <= 100 && !this._isGenericText(label)) {\n return label;\n }\n }\n const fullText = (_a = container.textContent) == null ? void 0 : _a.trim();\n if (fullText && fullText.length >= 2 && fullText.length <= 100 && !this._isGenericText(fullText)) {\n return fullText;\n }\n return null;\n }\n /**\n * Extract identifying text from a row (profile name, folder name, etc.)\n * Looks for short, meaningful text that identifies the row.\n *\n * SKYR-3706: Two-pass scan over Strategies 1+2. First pass excludes\n * dynamic-looking text (UUIDs, 6+ digit ids — `_isDynamic`); second pass\n * allows them as a last resort. Without this, a row whose first cell\n * holds a backend-assigned id like \"99925484\" gets that id picked over\n * the workflow name \"WF1\" in a later cell, producing a row filter that\n * matches the recording's run only.\n */\n _getRowIdentifyingText(row) {\n var _a;\n const isAcceptable = (text, allowDynamic) => {\n if (!text) return false;\n if (text.length < 2 || text.length > 100) return false;\n if (this._isGenericText(text)) return false;\n if (!allowDynamic && this._isDynamic(text)) return false;\n return true;\n };\n for (const allowDynamic of [false, true]) {\n for (const link of row.querySelectorAll(\"a\")) {\n const text = (_a = link.textContent) == null ? void 0 : _a.trim();\n if (isAcceptable(text, allowDynamic)) {\n return text;\n }\n }\n for (const cell of row.querySelectorAll('[role=\"cell\"], [role=\"gridcell\"], td')) {\n const text = this._getDirectTextContent(cell);\n if (isAcceptable(text, allowDynamic)) {\n return text;\n }\n }\n }\n const testIdElement = row.querySelector(\"[data-testid], [data-test-id]\");\n if (testIdElement) {\n const testId = this._getTestId(testIdElement);\n if (testId && !this._isDynamic(testId) && !/^(item|row|cell|grid)/i.test(testId)) {\n return testId;\n }\n }\n const ariaLabelElement = row.querySelector('[aria-label*=\"menu for\"], [aria-label*=\"actions for\"]');\n if (ariaLabelElement) {\n const ariaLabel = ariaLabelElement.getAttribute(\"aria-label\");\n const match = ariaLabel == null ? void 0 : ariaLabel.match(/(?:menu|actions)\\s+for\\s+(.+)$/i);\n if (match && match[1]) {\n return match[1].trim();\n }\n }\n return null;\n }\n /**\n * For row containers, try to build an internal:has filter using a link child element.\n * This produces .filter({ has: getByRole(\"link\", { name: \"X\", exact: true }) })\n * which is more stable than has-text because it avoids matching against the full\n * row accessible name that may contain dynamic IDs or other volatile content.\n * Returns the JSON-encoded inner selector string for internal:has, or null.\n */\n _getRowHasFilter(container, containerSelector) {\n var _a;\n const role = container.getAttribute(\"role\");\n if (role !== \"row\" && container.tagName !== \"TR\" && !container.classList.contains(\"a-data-table__row\")) {\n return null;\n }\n const links = container.querySelectorAll(\"a\");\n for (const link of links) {\n const text = (_a = link.textContent) == null ? void 0 : _a.trim();\n if (!text || text.length < 2 || text.length > 100 || this._isGenericText(text)) {\n continue;\n }\n const escapedName = text.replace(/\\\\/g, \"\\\\\\\\\").replace(/\"/g, '\\\\\"');\n const innerSelector = `internal:role=link[name=\"${escapedName}\"s]`;\n const testSelector = `${containerSelector} >> internal:has=${JSON.stringify(innerSelector)}`;\n const verification = this._verifySelector(testSelector);\n if (verification.count === 1) {\n log(\"Has-filter: Found unique link text:\", text);\n return JSON.stringify(innerSelector);\n }\n log(\"Has-filter: Link text not unique:\", text, \"matches:\", verification.count);\n }\n return null;\n }\n /**\n * Re-anchor a positional container on its enclosing role=row when the row\n * carries a stable, non-volatile name. Only fires when the container is\n * strictly INSIDE a row (e.g. a gridcell) — row/tr containers already have\n * the has-filter and text-filter strategies.\n *\n * Anchor preference (each tried exact \"s\" first, then substring \"i\" —\n * see the comment at the match sites):\n * 1. The row's own explicit label (aria-label / aria-labelledby):\n * internal:role=row[name=\"X\"s|i] >> <relative>\n * 2. The rowheader's accessible name via has-filter (content-derived row\n * names concatenate volatile cells like modified dates):\n * [role=\"row\"] >> internal:has=\"internal:role=rowheader[name=\\\"X\\\"s|i]\" >> <relative>\n *\n * Returns the full scoped selector (verified unique) or null.\n */\n _getRowAnchorSelector(containerEl, target) {\n var _a, _b;\n let rowEl = containerEl.parentElement;\n while (rowEl && rowEl !== target.ownerDocument.body) {\n if (rowEl.getAttribute(\"role\") === \"row\" || rowEl.tagName === \"TR\") break;\n rowEl = rowEl.parentElement;\n }\n if (!rowEl || rowEl === target.ownerDocument.body) return null;\n let relative = null;\n const role = this._getRole(target);\n if (role) {\n const roleSel = `internal:role=${role}`;\n try {\n const parsed = this._injectedScript.parseSelector(roleSel);\n if (this._injectedScript.querySelectorAll(parsed, rowEl).length === 1) {\n relative = roleSel;\n }\n } catch {\n }\n }\n if (!relative) {\n relative = this._generateRelativeSelector(rowEl, target) || null;\n }\n if (!relative) {\n log(\"Row anchor: no relative selector within row\");\n return null;\n }\n const isStableAnchorName = (name) => {\n if (!name) return false;\n if (name.length < 2 || name.length > 50) return false;\n if (this._isGenericText(name)) return false;\n if (this._isDynamic(name)) return false;\n if (this._hasVolatileText(name)) return false;\n return true;\n };\n let explicitName = rowEl.getAttribute(\"aria-label\");\n if (!explicitName) {\n const labelledBy = rowEl.getAttribute(\"aria-labelledby\");\n if (labelledBy) {\n explicitName = ((_b = (_a = rowEl.ownerDocument.getElementById(labelledBy)) == null ? void 0 : _a.textContent) == null ? void 0 : _b.trim()) || null;\n }\n }\n if (isStableAnchorName(explicitName)) {\n for (const flag of [\"s\", \"i\"]) {\n const sel = `internal:role=row[name=${this._quoteCSSAttributeValue(explicitName)}${flag}] >> ${relative}`;\n if (this._verifySelector(sel).valid) {\n log(\"Row anchor: explicit row label ->\", sel);\n return sel;\n }\n }\n log(\"Row anchor: explicit row label not unique\");\n }\n const rowheader = rowEl.querySelector('[role=\"rowheader\"], th');\n if (rowheader) {\n const headerName = this._getAccessibleName(rowheader);\n if (isStableAnchorName(headerName)) {\n const rowSelector = rowEl.tagName === \"TR\" ? \"tr\" : '[role=\"row\"]';\n for (const flag of [\"s\", \"i\"]) {\n const inner = `internal:role=rowheader[name=${this._quoteCSSAttributeValue(headerName)}${flag}]`;\n const sel = `${rowSelector} >> internal:has=${JSON.stringify(inner)} >> ${relative}`;\n if (this._verifySelector(sel).valid) {\n log(\"Row anchor: rowheader has-filter ->\", sel);\n return sel;\n }\n }\n log(\"Row anchor: rowheader has-filter not unique\");\n }\n }\n return null;\n }\n /**\n * Get direct text content of an element, excluding nested elements\n */\n _getDirectTextContent(element) {\n let text = \"\";\n for (const node of element.childNodes) {\n if (node.nodeType === Node.TEXT_NODE) {\n text += node.textContent || \"\";\n }\n }\n return text.trim();\n }\n /**\n * Check if text is too generic to be a good identifier\n */\n _isGenericText(text) {\n const genericPatterns = [\n /^(edit|delete|view|open|close|save|cancel|submit|ok|yes|no)$/i,\n /^(item|row|cell|column|header|footer)$/i,\n /^(loading|please wait|...)$/i,\n /^\\d{1,5}$/,\n // Short positional numbers (e.g. row index \"1\", \"42\")\n // Longer numeric strings (6+ digits) are allowed through — they may be stable\n // identifiers (e.g. 識別コード). Uniqueness is verified downstream.\n /^[\\s\\-_]+$/\n // Just whitespace/separators\n ];\n return genericPatterns.some((p) => p.test(text));\n }\n /**\n * Escape text for use in has-text filter (handle quotes and special chars)\n */\n _escapeTextFilter(text) {\n return text.replace(/\\\\/g, \"\\\\\\\\\").replace(/\"/g, '\\\\\"');\n }\n // ==========================================================================\n // Step 4: Generate Relative Selector\n // ==========================================================================\n _generateRelativeSelector(container, target) {\n var _a;\n if (container === target || !container.contains(target)) return \"\";\n log(\"_generateRelativeSelector for:\", target.tagName, target.className);\n const svgRelatedTags = [\"svg\", \"path\", \"circle\", \"rect\", \"line\", \"polygon\", \"polyline\", \"ellipse\", \"g\", \"use\"];\n if (svgRelatedTags.includes(target.tagName.toLowerCase())) {\n log(\"Target is SVG or SVG child, walking up to find clickable ancestor\");\n const clickableAncestor = this._findClickableAncestor(container, target);\n if (clickableAncestor) {\n log(\"Found clickable ancestor:\", clickableAncestor.tagName, clickableAncestor.className);\n target = clickableAncestor;\n }\n }\n const testId = this._getTestId(target);\n if (testId && !this._isFragileTestId(testId)) {\n return this._buildTestIdSelector(target, testId);\n }\n const role = this._getRole(target);\n const ariaLabel = target.getAttribute(\"aria-label\");\n if (role && ariaLabel) {\n const explicitRole = target.getAttribute(\"role\");\n const roleSel = explicitRole ? `[role=${this._quoteCSSAttributeValue(role)}][aria-label=${this._quoteCSSAttributeValue(ariaLabel)}]` : `internal:role=${role}[name=${this._quoteCSSAttributeValue(ariaLabel)}i]`;\n try {\n const parsed = this._injectedScript.parseSelector(roleSel);\n const matches = this._injectedScript.querySelectorAll(parsed, container);\n if (matches.length === 1) {\n log(\"Strategy 2: role+aria-label ->\", roleSel);\n return roleSel;\n }\n log(\"Strategy 2: not unique in container, matches:\", matches.length);\n } catch {\n log(\"Strategy 2: selector parse failed for\", roleSel);\n }\n }\n if (role) {\n const sel = `[role=${this._quoteCSSAttributeValue(role)}]`;\n if (container.querySelectorAll(sel).length === 1) {\n return sel;\n }\n }\n if (ariaLabel) {\n const sel = `[aria-label=${this._quoteCSSAttributeValue(ariaLabel)}]`;\n if (container.querySelectorAll(sel).length === 1) {\n return sel;\n }\n }\n if (role === \"button\" || role === \"link\" || role === \"cell\" || role === \"gridcell\" || role === \"columnheader\" || role === \"rowheader\") {\n const accessibleName = this._getAccessibleName(target);\n if (accessibleName && accessibleName.length >= 2 && accessibleName.length <= 50 && !this._isGenericText(accessibleName) && !this._isDynamic(accessibleName)) {\n const roleSelector = `internal:role=${role}[name=${this._quoteCSSAttributeValue(accessibleName)}i]`;\n try {\n const parsed = this._injectedScript.parseSelector(roleSelector);\n const matches = this._injectedScript.querySelectorAll(parsed, container);\n if (matches.length === 1) {\n log(\"Strategy 4b: role with accessible name ->\", roleSelector);\n return roleSelector;\n }\n log(\"Strategy 4b: not unique in container, matches:\", matches.length);\n } catch {\n log(\"Strategy 4b: selector parse failed\");\n }\n }\n }\n if (target.tagName === \"INPUT\") {\n const allInputs = container.querySelectorAll(\"input\");\n log(\"Input strategy: found\", allInputs.length, \"inputs in container\");\n if (allInputs.length === 1) {\n log(\"Using input selector (single input in container)\");\n return \"input\";\n }\n const type = target.type || \"text\";\n const sel = `input[type=${this._quoteCSSAttributeValue(type)}]`;\n const typeMatches = container.querySelectorAll(sel);\n if (typeMatches.length === 1) {\n log(\"Using input[type] selector\");\n return sel;\n }\n log(\"Multiple inputs found, using input anyway for form container\");\n return \"input\";\n }\n if (container.tagName === \"TR\") {\n const td = target.tagName === \"TD\" ? target : target.closest(\"td\");\n if (td && container.contains(td)) {\n const interactiveAncestor = target !== td ? target.closest(\"a, button, input, select, textarea\") : null;\n const isInteractive = interactiveAncestor && td.contains(interactiveAncestor);\n if (!isInteractive) {\n const cells = container.querySelectorAll(\":scope > td\");\n const cellIndex = Array.from(cells).indexOf(td);\n if (cellIndex >= 0) {\n log(\"Strategy 5b: table cell column index ->\", `td >> nth=${cellIndex}`);\n return `td >> nth=${cellIndex}`;\n }\n } else {\n log(\"Strategy 5b: skipping, target is inside interactive element:\", interactiveAncestor.tagName);\n }\n }\n }\n const tag = target.tagName.toLowerCase();\n if (container.querySelectorAll(tag).length === 1) {\n return tag;\n }\n const targetClassList = Array.from(target.classList);\n for (const cls of targetClassList) {\n if (/^(css|styled|sc|emotion|mui)-/.test(cls)) continue;\n if (/^Mui[A-Z]/.test(cls)) continue;\n if (cls.length < 3) continue;\n const sel = `.${this._escapeCSS(cls)}`;\n if (container.querySelectorAll(sel).length === 1) {\n return sel;\n }\n }\n const textContent = (_a = target.textContent) == null ? void 0 : _a.trim();\n if (textContent && textContent.length >= 2 && textContent.length <= 100) {\n const cleanText = textContent.replace(/\\s+/g, \" \");\n if (!this._isGenericText(cleanText)) {\n const textSelector = `internal:text=\"${this._escapeTextFilter(cleanText)}\"i`;\n try {\n const parsed = this._injectedScript.parseSelector(textSelector);\n const matches = this._injectedScript.querySelectorAll(parsed, container);\n if (matches.length === 1) {\n log(\"Strategy 8: text content filter ->\", textSelector);\n return textSelector;\n }\n log(\"Strategy 8: text not unique in container, matches:\", matches.length);\n } catch {\n log(\"Strategy 8: selector parse failed\");\n }\n }\n }\n return \"\";\n }\n /**\n * For SVG child elements, walk up to find a clickable ancestor with better attributes\n * Stops at container boundary\n */\n _findClickableAncestor(container, target) {\n let current = target.parentElement;\n const presentationalRoles = [\"img\", \"presentation\", \"none\", \"graphics-symbol\"];\n while (current && current !== container && container.contains(current)) {\n const hasTestId = current.getAttribute(\"data-testid\") || current.getAttribute(\"data-test-id\");\n const role = current.getAttribute(\"role\");\n const hasInteractiveRole = role && !presentationalRoles.includes(role);\n const hasAriaLabel = current.getAttribute(\"aria-label\");\n const isClickable = [\"BUTTON\", \"A\", \"INPUT\", \"SELECT\"].includes(current.tagName);\n const hasClickHandler = current.hasAttribute(\"onclick\") || current.hasAttribute(\"data-click\");\n if (hasTestId || hasInteractiveRole || hasAriaLabel && isClickable || isClickable || hasClickHandler) {\n log(\n \"_findClickableAncestor found:\",\n current.tagName,\n \"testid:\",\n hasTestId,\n \"role:\",\n role,\n \"isClickable:\",\n isClickable\n );\n return current;\n }\n current = current.parentElement;\n }\n return null;\n }\n /**\n * Check if text contains volatile date content that changes over time\n * (modified-date columns and the like) — month-name forms (\"Apr 10, 2025\")\n * and relative words (\"Today\", \"2 days ago\"). Numeric dates and timestamps\n * are covered by _isDynamic/_hasDynamicSelector. Delegates to the shared\n * detector in volatileDate.ts so record-time scoping and selector\n * generation agree on what counts as volatile.\n */\n _hasVolatileText(text) {\n return hasVolatileDateFragment(text);\n }\n /**\n * Decide whether a data-testid is too fragile to pin as a selector part\n * (leaf relative selector or scoping container).\n *\n * `_isDynamic` alone only catches UUID / long-hex / 6+-digit values, so an\n * INDEX-suffixed testid (choice-card-1, package-name-6, invite-entry-email-0)\n * slips through and gets pinned — the \"1\"/\"6\"/\"0\" is a positional counter\n * that shifts when the list reorders or grows (SKYR-3840). `_isDynamicId`\n * already encodes the `[-_]\\d+$` index-suffix rule (plus the framework id\n * patterns) and applies equally to testids, so union the two: a testid is\n * fragile if EITHER classifier flags it. This mirrors the top-level\n * `_hasDynamicSelector` testid rule, which already rejects `[-_]\\d+`.\n */\n _isFragileTestId(testId) {\n return this._isDynamic(testId) || this._isDynamicId(testId);\n }\n _isDynamic(value) {\n if (/^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i.test(value)) return true;\n if (/[-_][0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i.test(value)) return true;\n const hexSuffix = value.match(/[-_]([0-9a-f]{8,})$/i);\n if (hexSuffix && /[0-9]/.test(hexSuffix[1])) return true;\n if (/^\\d{6,}$/.test(value)) return true;\n if (/^\\d{10,13}$/.test(value)) return true;\n return false;\n }\n _getRole(element) {\n var _a;\n const explicit = element.getAttribute(\"role\");\n if (explicit) return explicit;\n const tag = element.tagName.toLowerCase();\n const roles = {\n button: \"button\",\n a: \"link\",\n select: \"combobox\",\n textarea: \"textbox\",\n img: \"img\",\n tr: \"row\",\n // Headings carry the implicit ARIA role 'heading'. Clickable card/tile\n // widgets often use a roleless <h5> that holds a stable label (the card\n // title) — without this mapping _getRole returns null and the role+name\n // anchor strategies (relative 4b, alternative 5b) never fire, so the\n // recorder falls back to the card's index-suffixed testid (SKYR-3840).\n h1: \"heading\",\n h2: \"heading\",\n h3: \"heading\",\n h4: \"heading\",\n h5: \"heading\",\n h6: \"heading\"\n };\n if (tag === \"input\") {\n const type = ((_a = element.type) == null ? void 0 : _a.toLowerCase()) || \"text\";\n const inputRoles = {\n checkbox: \"checkbox\",\n radio: \"radio\",\n button: \"button\",\n submit: \"button\"\n };\n return inputRoles[type] || \"textbox\";\n }\n if (tag === \"td\") {\n const table = element.closest(\"table\");\n const tableRole = table == null ? void 0 : table.getAttribute(\"role\");\n return tableRole === \"grid\" || tableRole === \"treegrid\" ? \"gridcell\" : \"cell\";\n }\n if (tag === \"th\")\n return getAriaRole(element);\n return roles[tag] || null;\n }\n // ==========================================================================\n // Step 5: Verify Selector\n // ==========================================================================\n _verifySelector(selector) {\n try {\n const parsed = this._injectedScript.parseSelector(selector);\n const elements = this._injectedScript.querySelectorAll(parsed, this._injectedScript.document);\n return {\n valid: elements.length === 1,\n count: elements.length,\n elements: Array.from(elements)\n };\n } catch {\n return { valid: false, count: 0, elements: [] };\n }\n }\n // ==========================================================================\n // Alternative Selector Generation (Fallback for Dynamic IDs without Containers)\n // ==========================================================================\n /**\n * Generate an alternative stable selector when:\n * 1. The original selector contains dynamic/unstable IDs (e.g., #mui-6, #react-aria123)\n * 2. No scoping container was found (element is not in a repeating context)\n *\n * This handles cases like MUI form inputs that have auto-generated IDs but\n * also have stable attributes like name, aria-label, or placeholder.\n *\n * Priority order for form elements:\n * 1. input[name=\"...\"] - Most stable for form elements\n * 2. [aria-label=\"...\"] - Accessible and stable\n * 3. [placeholder=\"...\"] - Common for inputs\n * 4. input[type=\"...\"] - If unique on page\n */\n _generateAlternativeSelector(element, originalSelector) {\n var _a;\n log(\"_generateAlternativeSelector for:\", element.tagName, \"original:\", originalSelector);\n const tag = element.tagName.toUpperCase();\n let alternativeSelector = null;\n if (tag === \"INPUT\" || tag === \"TEXTAREA\" || tag === \"SELECT\") {\n const name = element.getAttribute(\"name\");\n if (name && !this._isDynamic(name)) {\n alternativeSelector = `${tag.toLowerCase()}[name=${this._quoteCSSAttributeValue(name)}]`;\n log(\"Alternative strategy 1: name attribute ->\", alternativeSelector);\n }\n }\n if (!alternativeSelector) {\n const ariaLabel = element.getAttribute(\"aria-label\");\n if (ariaLabel && ariaLabel.length >= 2 && ariaLabel.length <= 100) {\n alternativeSelector = `[aria-label=${this._quoteCSSAttributeValue(ariaLabel)}]`;\n log(\"Alternative strategy 2: aria-label ->\", alternativeSelector);\n }\n }\n if (!alternativeSelector && (tag === \"INPUT\" || tag === \"TEXTAREA\")) {\n const placeholder = element.placeholder;\n if (placeholder && placeholder.length >= 2 && placeholder.length <= 100) {\n alternativeSelector = `${tag.toLowerCase()}[placeholder=${this._quoteCSSAttributeValue(placeholder)}]`;\n log(\"Alternative strategy 3: placeholder ->\", alternativeSelector);\n }\n }\n if (!alternativeSelector && tag === \"INPUT\") {\n const type = element.type || \"text\";\n const specificTypes = [\"email\", \"password\", \"tel\", \"url\", \"search\", \"number\", \"date\", \"time\", \"datetime-local\", \"month\", \"week\", \"color\", \"file\"];\n if (specificTypes.includes(type)) {\n const typeSelector = `input[type=${this._quoteCSSAttributeValue(type)}]`;\n const verification2 = this._verifySelector(typeSelector);\n if (verification2.valid) {\n alternativeSelector = typeSelector;\n log(\"Alternative strategy 4: unique input type ->\", alternativeSelector);\n }\n }\n }\n if (!alternativeSelector && (tag === \"BUTTON\" || tag === \"INPUT\" && element.type === \"submit\")) {\n const buttonText = (_a = element.textContent) == null ? void 0 : _a.trim();\n if (buttonText && buttonText.length >= 2 && buttonText.length <= 50 && !this._isGenericText(buttonText)) {\n alternativeSelector = `internal:role=button[name=${this._quoteCSSAttributeValue(buttonText)}i]`;\n log(\"Alternative strategy 5: button text ->\", alternativeSelector);\n }\n }\n if (!alternativeSelector) {\n const role = this._getRole(element);\n const accessibleName = role ? this._getAccessibleName(element) : null;\n if (role && accessibleName && accessibleName.length >= 2 && accessibleName.length <= 50 && !this._isGenericText(accessibleName) && !this._isDynamic(accessibleName)) {\n alternativeSelector = `internal:role=${role}[name=${this._quoteCSSAttributeValue(accessibleName)}i]`;\n log(\"Alternative strategy 5b: role+name ->\", alternativeSelector);\n }\n }\n if (!alternativeSelector) {\n const title = element.getAttribute(\"title\");\n if (title && title.length >= 2 && title.length <= 100) {\n alternativeSelector = `[title=${this._quoteCSSAttributeValue(title)}]`;\n log(\"Alternative strategy 6: title ->\", alternativeSelector);\n }\n }\n if (!alternativeSelector) {\n log(\"No alternative selector found\");\n return null;\n }\n const verification = this._verifySelector(alternativeSelector);\n log(\"Alternative verification:\", verification.valid ? \"PASS\" : \"FAIL\", \"| Matches:\", verification.count);\n if (!verification.valid) {\n if (verification.count > 1 && tag) {\n const taggedSelector = `${tag.toLowerCase()}${alternativeSelector.startsWith(\"[\") ? alternativeSelector : \" \" + alternativeSelector}`;\n const taggedVerification = this._verifySelector(taggedSelector);\n if (taggedVerification.valid) {\n alternativeSelector = taggedSelector;\n log(\"Made unique by adding tag:\", alternativeSelector);\n } else {\n log(\"Alternative selector not unique, rejecting\");\n return null;\n }\n } else {\n log(\"Alternative selector not unique, rejecting\");\n return null;\n }\n }\n const result = {\n selector: alternativeSelector,\n container: null,\n elements: verification.elements,\n strategy: \"alternative\",\n description: `Alternative selector for dynamic ID: ${originalSelector} -> ${alternativeSelector}`,\n containerSelector: \"\",\n containerIndex: -1,\n relativeSelector: \"\",\n isAlternativeSelector: true\n };\n logTable({\n \"Original (unstable)\": originalSelector,\n \"Alternative (stable)\": alternativeSelector,\n \"Strategy\": \"alternative\",\n \"Reason\": \"Dynamic ID without container\"\n });\n return result;\n }\n // ==========================================================================\n // Link-Based Selector Generation (for non-unique links)\n // ==========================================================================\n /**\n * Try to generate a simple link-based selector for navigation elements.\n * This is preferred over container-based scoping for links because:\n * 1. href attributes are stable (tied to routing)\n * 2. Accessible names are semantic and stable\n *\n * Checks the element and its ancestors (up to 3 levels) for link elements.\n *\n * Strategy 7: a[href=\"...\"] - Most stable for navigation\n * Strategy 8: getByRole('link', { name: '...' }) - Semantic and accessible\n */\n _tryLinkSelector(element, originalSelector) {\n log(\"_tryLinkSelector checking:\", element.tagName);\n const linkElement = this._findLinkElement(element);\n if (!linkElement) {\n log(\"No link element found\");\n return null;\n }\n log(\"Found link element:\", linkElement.tagName, \"href:\", linkElement.getAttribute(\"href\"));\n let linkSelector = null;\n const href = linkElement.getAttribute(\"href\");\n if (href && this._isStableHref(href)) {\n const hrefSelector = `a[href=${this._quoteCSSAttributeValue(href)}]`;\n const verification2 = this._verifySelector(hrefSelector);\n if (verification2.valid) {\n linkSelector = hrefSelector;\n log(\"Strategy 7: href selector ->\", linkSelector);\n } else {\n log(\"Strategy 7: href not unique, matches:\", verification2.count);\n }\n }\n if (!linkSelector) {\n const accessibleName = this._getAccessibleName(linkElement);\n if (accessibleName && accessibleName.length >= 2 && accessibleName.length <= 50) {\n const roleSelector = `internal:role=link[name=${this._quoteCSSAttributeValue(accessibleName)}i]`;\n const verification2 = this._verifySelector(roleSelector);\n if (verification2.valid) {\n linkSelector = roleSelector;\n log(\"Strategy 8: role=link with name ->\", linkSelector);\n } else {\n log(\"Strategy 8: role=link not unique, matches:\", verification2.count);\n if (verification2.count > 1) {\n const exactRoleSelector = `internal:role=link[name=${this._quoteCSSAttributeValue(accessibleName)}]`;\n const exactVerification = this._verifySelector(exactRoleSelector);\n if (exactVerification.valid) {\n linkSelector = exactRoleSelector;\n log(\"Strategy 8b: role=link with exact name ->\", linkSelector);\n }\n }\n }\n }\n }\n if (!linkSelector) {\n log(\"No suitable link selector found\");\n return null;\n }\n const verification = this._verifySelector(linkSelector);\n const result = {\n selector: linkSelector,\n container: null,\n elements: verification.elements,\n strategy: \"alternative\",\n description: `Link selector: ${originalSelector} -> ${linkSelector}`,\n containerSelector: \"\",\n containerIndex: -1,\n relativeSelector: \"\",\n isAlternativeSelector: true\n };\n logTable({\n \"Original\": originalSelector,\n \"Link selector\": linkSelector,\n \"Strategy\": \"link-based (7/8)\",\n \"Element\": linkElement.tagName\n });\n return result;\n }\n /**\n * Find the link element - either the element itself or an ancestor (up to 3 levels)\n * Returns the <a> tag or element with role=\"link\"\n */\n _findLinkElement(element) {\n let current = element;\n let depth = 0;\n const maxDepth = 3;\n while (current && depth <= maxDepth) {\n if (current.tagName === \"A\") {\n return current;\n }\n if (current.getAttribute(\"role\") === \"link\") {\n }\n current = current.parentElement;\n depth++;\n }\n return null;\n }\n /**\n * Check if href is stable (not dynamic/session-specific)\n */\n _isStableHref(href) {\n if (!href || href === \"#\" || href.startsWith(\"javascript:\")) {\n return false;\n }\n if (this._isDynamic(href)) {\n return false;\n }\n if (/[a-f0-9]{32,}/i.test(href)) {\n return false;\n }\n if (/\\/\\d{6,}(\\/|$)/.test(href)) {\n return false;\n }\n return true;\n }\n /**\n * Get the accessible name of an element.\n * This follows a simplified version of the accessible name computation:\n * 1. aria-label attribute\n * 2. aria-labelledby (resolve to referenced element's text)\n * 3. Text content (for links, buttons)\n */\n _getAccessibleName(element) {\n var _a, _b;\n const ariaLabel = element.getAttribute(\"aria-label\");\n if (ariaLabel && ariaLabel.trim()) {\n return ariaLabel.trim();\n }\n const labelledBy = element.getAttribute(\"aria-labelledby\");\n if (labelledBy) {\n const labelElement = element.ownerDocument.getElementById(labelledBy);\n if (labelElement) {\n const labelText = (_a = labelElement.textContent) == null ? void 0 : _a.trim();\n if (labelText) {\n return labelText;\n }\n }\n }\n const textContent = (_b = element.textContent) == null ? void 0 : _b.trim();\n if (textContent && textContent.length <= 100) {\n return textContent.replace(/\\s+/g, \" \");\n }\n return null;\n }\n};\nfunction escapeCSS(value) {\n if (typeof CSS !== \"undefined\" && CSS.escape) {\n return CSS.escape(value);\n }\n return value.replace(/([!\"#$%&'()*+,.\\/:;<=>?@[\\\\\\]^`{|}~])/g, \"\\\\$1\");\n}\nfunction quoteCSSAttributeValue(text) {\n return `\"${text.replace(/[\"\\\\]/g, (char) => \"\\\\\" + char)}\"`;\n}\nfunction applyScopingHook(injectedScript, element, selector, elements) {\n const scoping = new ScopingHandler(injectedScript);\n return scoping.applyScopingHook(element, selector, elements);\n}\n\n// packages/injected/src/recorder/skyramp/nestedElementHandler.ts\nvar NestedElementHandler = class {\n constructor(document2) {\n this._enabled = false;\n this._savedButtonRoles = /* @__PURE__ */ new Map();\n this._hiddenDuplicateButtons = [];\n this._document = document2;\n }\n get enabled() {\n return this._enabled;\n }\n toggle() {\n this._enabled = !this._enabled;\n if (this._enabled) {\n this._enableNestedElementAccess();\n } else {\n this._disableNestedElementAccess();\n }\n }\n _enableNestedElementAccess() {\n const buttonsWithRole = this._document.querySelectorAll('[role=\"button\"]');\n buttonsWithRole.forEach((element) => {\n const role = element.getAttribute(\"role\");\n if (element.children.length > 0 && role) {\n this._savedButtonRoles.set(element, role);\n element.removeAttribute(\"role\");\n const nestedButtons = element.querySelectorAll(\"button\");\n nestedButtons.forEach((nestedButton) => {\n this._createDuplicateButton(nestedButton, element);\n });\n }\n });\n }\n _disableNestedElementAccess() {\n this._savedButtonRoles.forEach((role, element) => {\n element.setAttribute(\"role\", role);\n });\n this._savedButtonRoles.clear();\n this._hiddenDuplicateButtons.forEach((button) => {\n button.remove();\n });\n this._hiddenDuplicateButtons = [];\n }\n _createDuplicateButton(nestedButton, wrapperElement) {\n var _a;\n const ariaLabel = nestedButton.getAttribute(\"aria-label\");\n const textContent = (_a = nestedButton.textContent) == null ? void 0 : _a.trim();\n const accessibleName = ariaLabel || textContent;\n if (!accessibleName)\n return;\n const duplicate = this._document.createElement(\"button\");\n duplicate.textContent = (textContent || \"\") + \" dup\";\n if (ariaLabel)\n duplicate.setAttribute(\"aria-label\", ariaLabel + \" dup\");\n duplicate.style.cssText = \"position: absolute !important; left: -9999px !important; width: 1px !important; height: 1px !important; overflow: hidden !important; pointer-events: none !important;\";\n duplicate.setAttribute(\"tabindex\", \"-1\");\n duplicate.setAttribute(\"data-pw-nested-button-duplicate\", \"true\");\n duplicate.disabled = true;\n wrapperElement.appendChild(duplicate);\n this._hiddenDuplicateButtons.push(duplicate);\n }\n /**\n * Handle a click on a nested element within a checkbox/radio tile.\n * Returns the scoped selector and auto-disable flag, or null if not a nested click.\n */\n handleNestedClick(clickedElement, hoveredModel, injectedScript, testIdAttributeName) {\n let parentElement = null;\n if (hoveredModel.elements && hoveredModel.elements.length > 0) {\n parentElement = hoveredModel.elements[0];\n }\n const isChildClick = parentElement && clickedElement !== parentElement && parentElement.contains(clickedElement);\n if (!isChildClick) {\n if (this._isInsideButtonWrapperWithNativeInput(clickedElement)) {\n return {\n targetSelector: hoveredModel.selector,\n shouldAutoDisable: true\n };\n }\n return null;\n }\n const parentRole = parentElement.getAttribute(\"role\");\n if (parentRole !== \"checkbox\" && parentRole !== \"radio\") {\n if (!this._isInsideButtonWrapperWithNativeInput(parentElement)) {\n return null;\n }\n }\n const generated = injectedScript.generateSelector(clickedElement, {\n testIdAttributeName\n });\n let targetSelector;\n if (generated.selector === hoveredModel.selector) {\n const childSelector = this._buildChildSelector(clickedElement, parentElement, testIdAttributeName);\n if (!childSelector) {\n return null;\n }\n targetSelector = `${hoveredModel.selector} >> ${childSelector}`;\n } else {\n const parentSelector = hoveredModel.selector;\n const childSelector = generated.selector;\n if (parentSelector && childSelector && !childSelector.startsWith(parentSelector) && !childSelector.includes(\">>\")) {\n targetSelector = `${parentSelector} >> ${childSelector}`;\n } else {\n targetSelector = childSelector;\n }\n }\n return {\n targetSelector,\n shouldAutoDisable: true\n };\n }\n /**\n * Build a CSS selector for a child element within a parent container.\n * Handles SVG elements and their children specially.\n */\n _buildChildSelector(clickedElement, parentElement, testIdAttr) {\n const isSvgElement = (el) => el.tagName.toLowerCase() === \"svg\";\n const isSvgChild = (el) => {\n const tag = el.tagName.toLowerCase();\n return tag === \"path\" || tag === \"g\" || tag === \"circle\" || tag === \"rect\" || tag === \"polygon\" || tag === \"line\" || tag === \"polyline\" || tag === \"ellipse\";\n };\n let targetElement = clickedElement;\n if (isSvgChild(clickedElement)) {\n let parent = clickedElement.parentElement;\n while (parent && parent !== parentElement) {\n if (isSvgElement(parent) && parent.classList.length > 0) {\n const classes = Array.from(parent.classList);\n if (classes.some((c) => c.includes(\"chevron\") || c.includes(\"icon\") || c.includes(\"expandable\"))) {\n targetElement = parent;\n break;\n }\n }\n if (!isSvgElement(parent) && !isSvgChild(parent) && parent.classList.length > 0) {\n targetElement = parent;\n break;\n }\n parent = parent.parentElement;\n }\n } else if (isSvgElement(clickedElement) && clickedElement.classList.length === 0) {\n let parent = clickedElement.parentElement;\n while (parent && parent !== parentElement) {\n if (parent.classList.length > 0) {\n targetElement = parent;\n break;\n }\n parent = parent.parentElement;\n }\n }\n if (targetElement.hasAttribute(testIdAttr)) {\n const attrValue = targetElement.getAttribute(testIdAttr) || \"\";\n return `[${testIdAttr}=${quoteCSSAttributeValue(attrValue)}]`;\n }\n if (targetElement.classList.length > 0) {\n const classes = Array.from(targetElement.classList);\n const meaningfulClasses = classes.filter(\n (c) => c.includes(\"expand\") || c.includes(\"chevron\") || c.includes(\"badge\") || c.includes(\"checkmark\") || c.includes(\"heading\") || c.includes(\"status\") || c.includes(\"icon\") || c.includes(\"button\") || c.includes(\"title\")\n );\n if (meaningfulClasses.length > 0) {\n const specificClass = meaningfulClasses.find(\n (c) => c.includes(\"chevron\") || c.includes(\"checkmark\") || c.includes(\"badge\")\n ) || meaningfulClasses[0];\n return \".\" + escapeCSS(specificClass);\n } else if (classes.length > 0) {\n return \".\" + escapeCSS(classes[0]);\n }\n }\n return targetElement.tagName.toLowerCase();\n }\n /**\n * Check if an element is inside a former role=\"button\" wrapper (stripped by\n * _enableNestedElementAccess) that also contains a native checkbox or radio input.\n */\n _isInsideButtonWrapperWithNativeInput(element) {\n let ancestor = element;\n while (ancestor) {\n if (this._savedButtonRoles.has(ancestor)) {\n return !!ancestor.querySelector('input[type=\"checkbox\"], input[type=\"radio\"]');\n }\n ancestor = ancestor.parentElement;\n }\n return false;\n }\n cleanup() {\n if (this._enabled) {\n this._disableNestedElementAccess();\n this._enabled = false;\n }\n }\n};\n\n// packages/injected/src/recorder/skyramp/pdfJsViewer.ts\nvar PdfJsViewer = class _PdfJsViewer {\n constructor(container) {\n this._pdfDoc = null;\n this._canvasElements = [];\n this._thumbnailElements = [];\n this._isRendering = false;\n this._toolbar = null;\n this._sidebar = null;\n this._mainContent = null;\n this._currentPage = 1;\n this._pageDisplay = null;\n this._zoomDisplay = null;\n this._currentZoom = 1;\n // 100%\n this._rotation = 0;\n // 0, 90, 180, 270 degrees\n this._pdfDataUrl = \"\";\n // Store for download\n this._filename = \"Document.pdf\";\n this._moreMenuElement = null;\n this._moreMenuCleanup = null;\n this._twoPageView = false;\n this._annotationsVisible = true;\n this._container = container;\n }\n /**\n * Loads PDF.js library from CDN if not already loaded\n */\n static async loadPdfJs() {\n if (window.pdfjsLib) {\n return;\n }\n console.log(\"[PDF.js] Loading PDF.js library from CDN...\");\n const script = document.createElement(\"script\");\n script.src = \"https://cdnjs.cloudflare.com/ajax/libs/pdf.js/3.11.174/pdf.min.js\";\n return new Promise((resolve, reject) => {\n script.onload = () => {\n if (!window.pdfjsLib) {\n reject(new Error(\"PDF.js loaded but pdfjsLib not available\"));\n return;\n }\n window.pdfjsLib.GlobalWorkerOptions.workerSrc = \"https://cdnjs.cloudflare.com/ajax/libs/pdf.js/3.11.174/pdf.worker.min.js\";\n console.log(\"[PDF.js] \\u2705 PDF.js library loaded successfully\");\n resolve();\n };\n script.onerror = () => reject(new Error(\"Failed to load PDF.js from CDN\"));\n document.head.appendChild(script);\n });\n }\n /**\n * Renders a PDF from a data URL\n */\n async renderPdf(config) {\n const { pdfDataUrl, filename, onReady, onError } = config;\n try {\n this._pdfDataUrl = pdfDataUrl;\n this._filename = filename || \"Document.pdf\";\n await _PdfJsViewer.loadPdfJs();\n console.log(\"[PDF.js] Rendering PDF...\");\n this._isRendering = true;\n const loadingTask = window.pdfjsLib.getDocument(pdfDataUrl);\n this._pdfDoc = await loadingTask.promise;\n console.log(`[PDF.js] PDF loaded: ${this._pdfDoc.numPages} pages`);\n if (document.documentElement) {\n document.documentElement.style.height = \"auto\";\n }\n if (document.body) {\n document.body.style.margin = \"0\";\n document.body.style.padding = \"0\";\n document.body.style.width = \"100%\";\n document.body.style.height = \"auto\";\n document.body.style.overflow = \"auto\";\n }\n this._container.innerHTML = \"\";\n this._canvasElements = [];\n this._thumbnailElements = [];\n this._container.style.cssText = `\n width: 100%;\n min-height: 100vh;\n display: flex;\n flex-direction: column;\n background-color: #525252;\n margin: 0;\n padding: 0;\n `;\n this._toolbar = document.createElement(\"div\");\n this._toolbar.style.cssText = `\n width: 100%;\n height: 56px;\n background-color: #4a4a4a;\n color: #e8eaed;\n display: flex;\n align-items: center;\n justify-content: space-between;\n padding: 0 8px;\n box-sizing: border-box;\n font-family: 'Roboto', Arial, sans-serif;\n font-size: 14px;\n flex-shrink: 0;\n border-bottom: 1px solid #2a2a2a;\n position: sticky;\n top: 0;\n z-index: 10;\n `;\n const leftSection = document.createElement(\"div\");\n leftSection.style.cssText = \"display: flex; align-items: center; gap: 12px;\";\n const menuBtn = this._createToolbarButton(\"\\u2261\", \"Menu\", () => {\n if (this._sidebar) {\n const isHidden = this._sidebar.style.display === \"none\";\n this._sidebar.style.display = isHidden ? \"block\" : \"none\";\n }\n });\n menuBtn.style.fontSize = \"24px\";\n leftSection.appendChild(menuBtn);\n const filenameDisplay = document.createElement(\"div\");\n filenameDisplay.textContent = filename || \"Document.pdf\";\n filenameDisplay.style.cssText = `\n color: #e8eaed;\n font-size: 14px;\n font-weight: 400;\n margin-left: 4px;\n `;\n leftSection.appendChild(filenameDisplay);\n const centerSection = document.createElement(\"div\");\n centerSection.style.cssText = \"display: flex; align-items: center; gap: 12px;\";\n const pageNav = document.createElement(\"div\");\n pageNav.style.cssText = \"display: flex; align-items: center; gap: 8px;\";\n const pageDisplay = document.createElement(\"span\");\n pageDisplay.textContent = `1 / ${this._pdfDoc.numPages}`;\n pageDisplay.style.cssText = \"color: #e8eaed; font-size: 13px; min-width: 50px; text-align: center;\";\n pageNav.appendChild(pageDisplay);\n centerSection.appendChild(pageNav);\n const divider1 = document.createElement(\"div\");\n divider1.style.cssText = \"width: 1px; height: 24px; background-color: #5f5f5f;\";\n centerSection.appendChild(divider1);\n const zoomControls = document.createElement(\"div\");\n zoomControls.style.cssText = \"display: flex; align-items: center; gap: 8px;\";\n const zoomOutBtn = this._createToolbarButton(\"\\u2212\", \"Zoom out\", () => {\n this._zoom(this._currentZoom - 0.1);\n });\n zoomControls.appendChild(zoomOutBtn);\n const zoomDisplay = document.createElement(\"span\");\n zoomDisplay.textContent = \"100%\";\n zoomDisplay.style.cssText = \"color: #e8eaed; font-size: 13px; min-width: 45px; text-align: center; cursor: pointer;\";\n zoomDisplay.title = \"Reset zoom to 100%\";\n zoomDisplay.addEventListener(\"click\", () => {\n this._zoom(1);\n });\n zoomControls.appendChild(zoomDisplay);\n const zoomInBtn = this._createToolbarButton(\"+\", \"Zoom in\", () => {\n this._zoom(this._currentZoom + 0.1);\n });\n zoomControls.appendChild(zoomInBtn);\n centerSection.appendChild(zoomControls);\n const divider2 = document.createElement(\"div\");\n divider2.style.cssText = \"width: 1px; height: 24px; background-color: #5f5f5f;\";\n centerSection.appendChild(divider2);\n const fitBtn = this._createToolbarButton(\"\\u22A1\", \"Fit to page\", () => {\n this._fitToPage();\n });\n fitBtn.style.fontSize = \"18px\";\n centerSection.appendChild(fitBtn);\n const rotateBtn = this._createToolbarButton(\"\\u21BB\", \"Rotate clockwise\", () => {\n this._rotate();\n });\n rotateBtn.style.fontSize = \"18px\";\n centerSection.appendChild(rotateBtn);\n const rightSection = document.createElement(\"div\");\n rightSection.style.cssText = \"display: flex; align-items: center; gap: 8px;\";\n const downloadBtn = this._createToolbarButton(\"\\u2B07\", \"Download\", () => {\n this._download();\n });\n downloadBtn.style.fontSize = \"18px\";\n rightSection.appendChild(downloadBtn);\n const printBtn = this._createToolbarButton(\"\\u{1F5A8}\", \"Print\", () => {\n this._print();\n });\n printBtn.style.fontSize = \"16px\";\n rightSection.appendChild(printBtn);\n let moreBtn;\n moreBtn = this._createToolbarButton(\"\\u22EE\", \"More options\", () => {\n this._toggleMoreMenu(moreBtn);\n });\n moreBtn.style.fontSize = \"20px\";\n rightSection.appendChild(moreBtn);\n this._toolbar.appendChild(leftSection);\n this._toolbar.appendChild(centerSection);\n this._toolbar.appendChild(rightSection);\n this._pageDisplay = pageDisplay;\n this._zoomDisplay = zoomDisplay;\n const contentWrapper = document.createElement(\"div\");\n contentWrapper.style.cssText = `\n width: 100%;\n flex: 1;\n display: flex;\n `;\n this._sidebar = document.createElement(\"div\");\n this._sidebar.style.cssText = `\n width: 294px;\n height: calc(100vh - 56px);\n overflow-y: auto;\n overflow-x: hidden;\n background-color: #3f3f3f;\n border-right: 1px solid #2a2a2a;\n padding: 20px 35px 20px 70px;\n box-sizing: border-box;\n flex-shrink: 0;\n position: sticky;\n top: 56px;\n align-self: flex-start;\n `;\n this._mainContent = document.createElement(\"div\");\n this._mainContent.style.cssText = `\n flex: 1;\n overflow: visible;\n background-color: #525252;\n position: relative;\n padding: 0;\n box-sizing: border-box;\n `;\n this._container.appendChild(this._toolbar);\n contentWrapper.appendChild(this._sidebar);\n contentWrapper.appendChild(this._mainContent);\n this._container.appendChild(contentWrapper);\n for (let pageNum = 1; pageNum <= this._pdfDoc.numPages; pageNum++) {\n await this._renderPage(pageNum);\n await this._renderThumbnail(pageNum);\n }\n this._setupScrollSync();\n this._isRendering = false;\n console.log(\"[PDF.js] \\u2705 All pages rendered successfully\");\n if (onReady) {\n onReady();\n }\n } catch (error) {\n this._isRendering = false;\n console.error(\"[PDF.js] \\u274C Failed to render PDF:\", error);\n if (onError) {\n onError(error);\n }\n }\n }\n /**\n * Injects the minimal CSS required by PDF.js renderTextLayer (once per document).\n * PDF.js relies on external CSS for `position: absolute` and `transform-origin` on\n * text layer spans — without it the spans are in normal flow and misaligned.\n */\n static _injectTextLayerCss() {\n if (document.getElementById(\"pw-pdf-text-layer-styles\"))\n return;\n const style = document.createElement(\"style\");\n style.id = \"pw-pdf-text-layer-styles\";\n style.textContent = `\n div[data-pw-pdf-text-layer] {\n line-height: 1;\n -webkit-text-size-adjust: none;\n -moz-text-size-adjust: none;\n text-size-adjust: none;\n forced-color-adjust: none;\n transform-origin: 0 0;\n }\n div[data-pw-pdf-text-layer] :is(span, br) {\n color: transparent;\n position: absolute;\n white-space: pre;\n cursor: text;\n transform-origin: 0% 0%;\n pointer-events: none;\n }\n div[data-pw-pdf-text-layer] span.markedContent {\n top: 0;\n height: 0;\n }\n `;\n document.head.appendChild(style);\n }\n /**\n * Renders a single page in the main content area\n */\n async _renderPage(pageNum) {\n if (!this._mainContent) return;\n const page = await this._pdfDoc.getPage(pageNum);\n const viewport = page.getViewport({ scale: 1 });\n const containerWidth = this._mainContent.clientWidth || 800;\n let scale;\n let wrapperMargin;\n if (this._twoPageView) {\n const availableWidth = (containerWidth / 2 - 32) * 0.95;\n scale = availableWidth / viewport.width * this._currentZoom;\n wrapperMargin = \"16px auto\";\n } else {\n const availableWidth = (containerWidth - 80) * 0.89;\n scale = availableWidth / viewport.width * this._currentZoom;\n wrapperMargin = \"16px 20px 16px 80px\";\n }\n const scaledViewport = page.getViewport({ scale });\n const canvasWrapper = document.createElement(\"div\");\n canvasWrapper.setAttribute(\"data-page-number\", pageNum.toString());\n canvasWrapper.style.cssText = `\n position: relative;\n margin: ${wrapperMargin};\n background: white;\n box-shadow: 0 1px 3px rgba(0, 0, 0, 0.3), 0 4px 8px rgba(0, 0, 0, 0.15);\n width: ${scaledViewport.width}px;\n height: ${scaledViewport.height}px;\n box-sizing: border-box;\n `;\n const canvas = document.createElement(\"canvas\");\n canvas.width = scaledViewport.width;\n canvas.height = scaledViewport.height;\n canvas.style.cssText = `\n display: block;\n width: 100%;\n height: 100%;\n `;\n canvasWrapper.appendChild(canvas);\n this._mainContent.appendChild(canvasWrapper);\n this._canvasElements.push(canvas);\n const context = canvas.getContext(\"2d\");\n if (!context) {\n throw new Error(\"Failed to get canvas 2D context\");\n }\n const renderContext = {\n canvasContext: context,\n viewport: scaledViewport\n };\n await page.render(renderContext).promise;\n try {\n _PdfJsViewer._injectTextLayerCss();\n const textContent = await page.getTextContent();\n const textLayer = document.createElement(\"div\");\n textLayer.setAttribute(\"data-pw-pdf-text-layer\", pageNum.toString());\n textLayer.style.cssText = `\n position: absolute;\n top: 0;\n left: 0;\n width: ${scaledViewport.width}px;\n height: ${scaledViewport.height}px;\n overflow: hidden;\n opacity: 0;\n `;\n textLayer.style.setProperty(\"--scale-factor\", String(scale));\n canvasWrapper.appendChild(textLayer);\n const renderTask = window.pdfjsLib.renderTextLayer({\n textContentSource: textContent,\n container: textLayer,\n viewport: scaledViewport,\n textDivs: []\n });\n await renderTask.promise;\n } catch (e) {\n console.warn(`[PDF.js] Text layer render failed for page ${pageNum}:`, e);\n }\n console.log(`[PDF.js] Rendered page ${pageNum}/${this._pdfDoc.numPages}`);\n }\n /**\n * Renders a thumbnail for the sidebar\n */\n async _renderThumbnail(pageNum) {\n if (!this._sidebar) return;\n const page = await this._pdfDoc.getPage(pageNum);\n const viewport = page.getViewport({ scale: 1 });\n const thumbnailWidth = 118;\n const scale = thumbnailWidth / viewport.width;\n const scaledViewport = page.getViewport({ scale });\n const thumbWrapper = document.createElement(\"div\");\n thumbWrapper.setAttribute(\"data-page-number\", pageNum.toString());\n thumbWrapper.style.cssText = `\n margin: 18px auto;\n background: white;\n cursor: pointer;\n border: 3px solid transparent;\n box-sizing: border-box;\n transition: border-color 0.15s;\n width: fit-content;\n box-shadow: 0 1px 3px rgba(0, 0, 0, 0.2);\n `;\n if (pageNum === 1) {\n thumbWrapper.style.borderColor = \"#1a73e8\";\n }\n const canvas = document.createElement(\"canvas\");\n canvas.width = scaledViewport.width;\n canvas.height = scaledViewport.height;\n canvas.style.cssText = \"display: block; width: 100%; height: auto;\";\n const label = document.createElement(\"div\");\n label.textContent = pageNum.toString();\n label.style.cssText = `\n text-align: center;\n color: #dadce0;\n font-size: 13px;\n padding: 5px;\n background: #3f3f3f;\n font-family: 'Roboto', Arial, sans-serif;\n `;\n thumbWrapper.appendChild(canvas);\n thumbWrapper.appendChild(label);\n this._sidebar.appendChild(thumbWrapper);\n this._thumbnailElements.push(thumbWrapper);\n thumbWrapper.addEventListener(\"click\", () => {\n this._scrollToPage(pageNum);\n });\n const context = canvas.getContext(\"2d\");\n if (!context) return;\n await page.render({\n canvasContext: context,\n viewport: scaledViewport\n }).promise;\n }\n /**\n * Scrolls to a specific page\n */\n _scrollToPage(pageNum) {\n if (!this._mainContent) return;\n const pageElement = this._mainContent.querySelector(`[data-page-number=\"${pageNum}\"]`);\n if (pageElement) {\n const rect = pageElement.getBoundingClientRect();\n window.scrollBy({ top: rect.top - 56, behavior: \"smooth\" });\n this._updateCurrentPage(pageNum);\n }\n }\n /**\n * Updates the current page highlight in sidebar (match Chrome's blue highlight)\n */\n _updateCurrentPage(pageNum) {\n if (this._currentPage === pageNum) return;\n if (this._thumbnailElements[this._currentPage - 1]) {\n this._thumbnailElements[this._currentPage - 1].style.borderColor = \"transparent\";\n }\n if (this._thumbnailElements[pageNum - 1]) {\n this._thumbnailElements[pageNum - 1].style.borderColor = \"#1a73e8\";\n }\n this._currentPage = pageNum;\n if (this._pageDisplay) {\n this._pageDisplay.textContent = `${pageNum} / ${this._pdfDoc.numPages}`;\n }\n }\n /**\n * Creates a toolbar button with consistent styling\n */\n _createToolbarButton(icon, title, onClick) {\n const button = document.createElement(\"button\");\n button.textContent = icon;\n button.title = title;\n button.style.cssText = `\n background: transparent;\n border: none;\n color: #e8eaed;\n cursor: pointer;\n padding: 6px 8px;\n border-radius: 4px;\n font-size: 16px;\n line-height: 1;\n display: flex;\n align-items: center;\n justify-content: center;\n min-width: 32px;\n height: 32px;\n transition: background-color 0.2s;\n `;\n button.addEventListener(\"mouseenter\", () => {\n button.style.backgroundColor = \"rgba(255, 255, 255, 0.1)\";\n });\n button.addEventListener(\"mouseleave\", () => {\n button.style.backgroundColor = \"transparent\";\n });\n button.addEventListener(\"click\", (e) => {\n e.preventDefault();\n onClick();\n });\n return button;\n }\n /**\n * Zoom to a specific level (1.0 = 100%)\n */\n async _zoom(newZoom) {\n if (!this._pdfDoc || !this._mainContent) {\n console.warn(\"[PDF.js] Cannot zoom: PDF not loaded\");\n return;\n }\n this._currentZoom = Math.max(0.25, Math.min(4, newZoom));\n if (this._zoomDisplay) {\n this._zoomDisplay.textContent = `${Math.round(this._currentZoom * 100)}%`;\n }\n console.log(`[PDF.js] Zooming to ${Math.round(this._currentZoom * 100)}%...`);\n await this._rerenderPages();\n console.log(\"[PDF.js] \\u2705 Zoom complete\");\n }\n /**\n * Re-renders all pages (used by zoom and two-page view toggle)\n */\n async _rerenderPages() {\n if (!this._pdfDoc || !this._mainContent) return;\n const scrollPercentage = window.scrollY / (document.body.scrollHeight || 1);\n if (this._twoPageView) {\n this._mainContent.style.cssText = `\n flex: 1;\n overflow: visible;\n background-color: #525252;\n position: relative;\n padding: 0;\n box-sizing: border-box;\n display: grid;\n grid-template-columns: 1fr 1fr;\n align-items: start;\n `;\n } else {\n this._mainContent.style.cssText = `\n flex: 1;\n overflow: visible;\n background-color: #525252;\n position: relative;\n padding: 0;\n box-sizing: border-box;\n `;\n }\n this._mainContent.innerHTML = \"\";\n this._canvasElements = [];\n for (let pageNum = 1; pageNum <= this._pdfDoc.numPages; pageNum++) {\n await this._renderPage(pageNum);\n }\n setTimeout(() => {\n window.scrollTo(0, scrollPercentage * document.body.scrollHeight);\n }, 100);\n }\n /**\n * Fit page to available width\n */\n _fitToPage() {\n if (!this._mainContent) return;\n this._zoom(1);\n console.log(\"[PDF.js] Fit to page\");\n }\n /**\n * Rotate PDF pages clockwise by 90 degrees\n */\n _rotate() {\n var _a;\n this._rotation = (this._rotation + 90) % 360;\n const pages = (_a = this._mainContent) == null ? void 0 : _a.querySelectorAll(\"[data-page-number]\");\n if (pages) {\n pages.forEach((page) => {\n page.style.transform = `rotate(${this._rotation}deg)`;\n });\n }\n console.log(`[PDF.js] Rotated to ${this._rotation} degrees`);\n }\n /**\n * Download the PDF file\n */\n _download() {\n if (!this._pdfDataUrl) {\n console.error(\"[PDF.js] No PDF data URL available for download\");\n return;\n }\n const link = document.createElement(\"a\");\n link.href = this._pdfDataUrl;\n link.download = this._filename;\n link.style.display = \"none\";\n document.body.appendChild(link);\n link.click();\n document.body.removeChild(link);\n console.log(`[PDF.js] Downloaded: ${this._filename}`);\n }\n /**\n * Print the PDF by rendering all canvas pages into a new window\n */\n _print() {\n if (!this._canvasElements.length) {\n console.error(\"[PDF.js] No pages rendered to print\");\n return;\n }\n const printWindow = window.open(\"\", \"_blank\");\n if (!printWindow) {\n console.warn(\"[PDF.js] Print window blocked by browser\");\n return;\n }\n const doc = printWindow.document;\n doc.write(`<!DOCTYPE html><html><head>\n <title>${this._filename}</title>\n <style>\n * { margin: 0; padding: 0; box-sizing: border-box; }\n body { background: white; }\n img { display: block; width: 100%; page-break-after: always; page-break-inside: avoid; }\n img:last-child { page-break-after: avoid; }\n </style>\n </head><body>`);\n for (const canvas of this._canvasElements) {\n const dataUrl = canvas.toDataURL(\"image/png\");\n doc.write(`<img src=\"${dataUrl}\">`);\n }\n doc.write(\"</body></html>\");\n doc.close();\n printWindow.onload = () => {\n printWindow.print();\n printWindow.close();\n };\n setTimeout(() => {\n if (!printWindow.closed) {\n printWindow.print();\n printWindow.close();\n }\n }, 1500);\n console.log(\"[PDF.js] Print window opened\");\n }\n /**\n * Toggles the \"more options\" dropdown menu matching Chrome's PDF viewer\n */\n _toggleMoreMenu(anchorElement) {\n if (this._moreMenuElement) {\n this._closeMoreMenu();\n return;\n }\n const menu = document.createElement(\"div\");\n this._moreMenuElement = menu;\n menu.style.cssText = `\n position: fixed;\n background: #202124;\n border-radius: 4px;\n box-shadow: 0 2px 10px rgba(0,0,0,0.6);\n z-index: 2147483648;\n min-width: 220px;\n padding: 4px 0;\n font-family: 'Roboto', Arial, sans-serif;\n font-size: 14px;\n color: #e8eaed;\n `;\n const rect = anchorElement.getBoundingClientRect();\n menu.style.top = `${rect.bottom + 4}px`;\n menu.style.right = `${window.innerWidth - rect.right}px`;\n const addMenuItem = (text, checked, onClick) => {\n const item = document.createElement(\"div\");\n item.style.cssText = `\n padding: 10px 16px 10px 44px;\n cursor: pointer;\n position: relative;\n white-space: nowrap;\n `;\n if (checked !== null) {\n const checkEl = document.createElement(\"span\");\n checkEl.textContent = checked ? \"\\u2713\" : \"\";\n checkEl.style.cssText = `\n position: absolute;\n left: 16px;\n top: 50%;\n transform: translateY(-50%);\n font-size: 14px;\n `;\n item.appendChild(checkEl);\n }\n const label = document.createElement(\"span\");\n label.textContent = text;\n item.appendChild(label);\n item.addEventListener(\"mouseenter\", () => {\n item.style.backgroundColor = \"rgba(255,255,255,0.1)\";\n });\n item.addEventListener(\"mouseleave\", () => {\n item.style.backgroundColor = \"transparent\";\n });\n item.addEventListener(\"click\", () => {\n this._closeMoreMenu();\n onClick();\n });\n menu.appendChild(item);\n return item;\n };\n const addDivider = () => {\n const d = document.createElement(\"div\");\n d.style.cssText = \"height: 1px; background: rgba(255,255,255,0.15); margin: 4px 0;\";\n menu.appendChild(d);\n };\n addMenuItem(\"Two page view\", this._twoPageView, () => {\n this._twoPageView = !this._twoPageView;\n this._rerenderPages();\n });\n addMenuItem(\"Annotations\", this._annotationsVisible, () => {\n this._annotationsVisible = !this._annotationsVisible;\n console.log(`[PDF.js] Annotations ${this._annotationsVisible ? \"shown\" : \"hidden\"}`);\n });\n addDivider();\n addMenuItem(\"Present\", null, () => {\n this._present();\n });\n addMenuItem(\"Document properties\", null, () => {\n this._showDocumentProperties();\n });\n const onOutsideClick = (e) => {\n if (!menu.contains(e.target) && e.target !== anchorElement) {\n this._closeMoreMenu();\n }\n };\n setTimeout(() => {\n document.addEventListener(\"mousedown\", onOutsideClick, true);\n this._moreMenuCleanup = () => document.removeEventListener(\"mousedown\", onOutsideClick, true);\n }, 0);\n document.body.appendChild(menu);\n }\n /**\n * Closes the more options dropdown menu\n */\n _closeMoreMenu() {\n if (this._moreMenuElement) {\n this._moreMenuElement.remove();\n this._moreMenuElement = null;\n }\n if (this._moreMenuCleanup) {\n this._moreMenuCleanup();\n this._moreMenuCleanup = null;\n }\n }\n /**\n * Present mode — not yet implemented.\n */\n _present() {\n console.log(\"[PDF.js] Present: not yet implemented\");\n }\n /**\n * Shows a dialog with document metadata matching Chrome's \"Document properties\"\n */\n async _showDocumentProperties() {\n if (!this._pdfDoc) return;\n let info = {};\n try {\n const metadata = await this._pdfDoc.getMetadata();\n info = metadata.info || {};\n } catch (e) {\n }\n let fileSize = \"-\";\n if (this._pdfDataUrl) {\n try {\n const base64 = this._pdfDataUrl.split(\",\")[1];\n if (base64) {\n const bytes = Math.ceil(base64.length * 3 / 4);\n fileSize = bytes >= 1024 * 1024 ? `${(bytes / (1024 * 1024)).toFixed(1)} MB` : `${(bytes / 1024).toFixed(1)} KB`;\n }\n } catch (e) {\n }\n }\n const formatPdfDate = (raw) => {\n if (!raw) return \"-\";\n const m = raw.match(/^D:(\\d{4})(\\d{2})(\\d{2})(\\d{2})(\\d{2})(\\d{2})/);\n if (!m) return raw;\n const date = /* @__PURE__ */ new Date(`${m[1]}-${m[2]}-${m[3]}T${m[4]}:${m[5]}:${m[6]}`);\n return isNaN(date.getTime()) ? raw : date.toLocaleString();\n };\n let pageSize = \"-\";\n try {\n const firstPage = await this._pdfDoc.getPage(1);\n const vp = firstPage.getViewport({ scale: 1 });\n const wIn = (vp.width / 72).toFixed(2);\n const hIn = (vp.height / 72).toFixed(2);\n const orientation = vp.width > vp.height ? \"landscape\" : \"portrait\";\n pageSize = `${wIn} \\xD7 ${hIn} in (${orientation})`;\n } catch (e) {\n }\n const sections = [\n [\n [\"File name:\", this._filename],\n [\"File size:\", fileSize]\n ],\n [\n [\"Title:\", info[\"Title\"] || \"-\"],\n [\"Author:\", info[\"Author\"] || \"-\"],\n [\"Subject:\", info[\"Subject\"] || \"-\"],\n [\"Keywords:\", info[\"Keywords\"] || \"-\"],\n [\"Created:\", formatPdfDate(info[\"CreationDate\"] || \"\")],\n [\"Modified:\", formatPdfDate(info[\"ModDate\"] || \"\")],\n [\"Application:\", info[\"Creator\"] || \"-\"]\n ],\n [\n [\"PDF producer:\", info[\"Producer\"] || \"-\"],\n [\"PDF version:\", info[\"PDFFormatVersion\"] || \"-\"],\n [\"Page count:\", `${this._pdfDoc.numPages}`],\n [\"Page size:\", pageSize]\n ],\n [\n [\"Fast web view:\", \"No\"]\n ]\n ];\n const overlay = document.createElement(\"div\");\n overlay.style.cssText = `\n position: fixed;\n top: 0; left: 0; right: 0; bottom: 0;\n background: rgba(0,0,0,0.5);\n z-index: 2147483649;\n display: flex;\n align-items: center;\n justify-content: center;\n `;\n const dialog = document.createElement(\"div\");\n dialog.style.cssText = `\n background: #3c4043;\n border-radius: 12px;\n padding: 24px 24px 16px;\n min-width: 380px;\n max-width: 500px;\n color: #e8eaed;\n font-family: 'Roboto', Arial, sans-serif;\n box-shadow: 0 4px 20px rgba(0,0,0,0.5);\n `;\n const titleEl = document.createElement(\"h3\");\n titleEl.textContent = \"Document properties\";\n titleEl.style.cssText = \"margin: 0 0 16px; font-size: 18px; font-weight: 500;\";\n dialog.appendChild(titleEl);\n const addRow = (label, value) => {\n const row = document.createElement(\"div\");\n row.style.cssText = \"display: flex; padding: 7px 0; font-size: 13px;\";\n const labelEl = document.createElement(\"span\");\n labelEl.textContent = label;\n labelEl.style.cssText = \"min-width: 140px; flex-shrink: 0;\";\n const valueEl = document.createElement(\"span\");\n valueEl.textContent = value;\n valueEl.style.wordBreak = \"break-all\";\n row.appendChild(labelEl);\n row.appendChild(valueEl);\n dialog.appendChild(row);\n };\n const addDivider = () => {\n const d = document.createElement(\"div\");\n d.style.cssText = \"height: 1px; background: rgba(255,255,255,0.15); margin: 6px 0;\";\n dialog.appendChild(d);\n };\n for (let i = 0; i < sections.length; i++) {\n for (const [label, value] of sections[i]) {\n addRow(label, value);\n }\n if (i < sections.length - 1) {\n addDivider();\n }\n }\n const closeBtn = document.createElement(\"button\");\n closeBtn.textContent = \"Close\";\n closeBtn.style.cssText = `\n display: block;\n margin: 20px 0 0 auto;\n padding: 10px 28px;\n background: #8ab4f8;\n border: none;\n border-radius: 24px;\n color: #202124;\n font-size: 14px;\n font-weight: 500;\n cursor: pointer;\n font-family: 'Roboto', Arial, sans-serif;\n `;\n closeBtn.addEventListener(\"click\", () => overlay.remove());\n dialog.appendChild(closeBtn);\n overlay.appendChild(dialog);\n overlay.addEventListener(\"click\", (e) => {\n if (e.target === overlay) overlay.remove();\n });\n document.body.appendChild(overlay);\n console.log(\"[PDF.js] Document properties dialog opened\");\n }\n /**\n * Sets up scroll synchronization between the document scroll and the sidebar thumbnail highlight.\n * Pages are in the document flow so we listen on window, not on _mainContent.\n */\n _setupScrollSync() {\n if (!this._mainContent) return;\n window.addEventListener(\"scroll\", () => {\n if (!this._mainContent) return;\n const pages = this._mainContent.querySelectorAll(\"[data-page-number]\");\n const toolbarBottom = 56;\n for (let i = 0; i < pages.length; i++) {\n const pageElement = pages[i];\n const rect = pageElement.getBoundingClientRect();\n if (rect.top <= toolbarBottom + 100 && rect.bottom > toolbarBottom) {\n const pageNum = parseInt(pageElement.getAttribute(\"data-page-number\") || \"1\");\n this._updateCurrentPage(pageNum);\n break;\n }\n }\n }, { passive: true });\n }\n /**\n * Cleanup resources\n */\n cleanup() {\n if (this._pdfDoc) {\n this._pdfDoc.destroy();\n this._pdfDoc = null;\n }\n this._closeMoreMenu();\n this._canvasElements = [];\n this._thumbnailElements = [];\n this._toolbar = null;\n this._sidebar = null;\n this._mainContent = null;\n this._currentPage = 1;\n this._container.innerHTML = \"\";\n }\n};\n\n// packages/injected/src/recorder/skyramp/pdfViewerHelper.ts\nvar PdfViewerHelper = class {\n /**\n * Fetches a PDF using Playwright's backend (bypasses CORS) and returns it as a data URL\n */\n static async fetchPdfViaBackend(pdfUrl) {\n try {\n console.log(\"[PW-PDF-VIEWER] Fetching PDF via Playwright backend:\", pdfUrl.substring(0, 100));\n if (!window.__pw_recorderFetchPdf) {\n throw new Error(\"__pw_recorderFetchPdf binding not available\");\n }\n const dataUrl = await window.__pw_recorderFetchPdf(pdfUrl);\n if (!dataUrl) {\n throw new Error(\"Backend returned null (fetch failed)\");\n }\n console.log(\"[PW-PDF-VIEWER] \\u2705 Successfully fetched PDF via backend:\", dataUrl.substring(0, 100));\n return dataUrl;\n } catch (error) {\n console.error(\"[PW-PDF-VIEWER] \\u274C Failed to fetch PDF via backend:\", error);\n return null;\n }\n }\n /**\n * Fetches a PDF via backend and renders it with PDF.js\n */\n static async renderPdfWithPdfJs(options) {\n const { pdfUrl, containerElement, onSuccess, onError } = options;\n try {\n console.log(\"[PW-PDF-VIEWER] Starting PDF render process...\");\n const dataUrl = await this.fetchPdfViaBackend(pdfUrl);\n if (!dataUrl) {\n throw new Error(\"Failed to fetch PDF from backend\");\n }\n console.log(\"[PW-PDF-VIEWER] \\u2705 PDF fetched, initializing PDF.js viewer...\");\n let filename = \"Document.pdf\";\n try {\n const url = new URL(pdfUrl);\n const pathname = url.pathname;\n const lastSlash = pathname.lastIndexOf(\"/\");\n if (lastSlash !== -1) {\n filename = pathname.substring(lastSlash + 1);\n filename = decodeURIComponent(filename);\n }\n } catch (e) {\n console.warn(\"[PW-PDF-VIEWER] Failed to extract filename from URL:\", e);\n }\n const viewer = new PdfJsViewer(containerElement);\n await viewer.renderPdf({\n pdfDataUrl: dataUrl,\n filename,\n onReady: () => {\n console.log(\"[PW-PDF-VIEWER] \\u2705 PDF rendered successfully!\");\n if (onSuccess) {\n onSuccess();\n }\n },\n onError: (error) => {\n console.error(\"[PW-PDF-VIEWER] \\u274C PDF.js render error:\", error);\n if (onError) {\n onError(error);\n }\n }\n });\n return true;\n } catch (error) {\n console.error(\"[PW-PDF-VIEWER] \\u274C Failed to render PDF:\", error);\n if (onError) {\n onError(error);\n }\n return false;\n }\n }\n};\n\n// packages/injected/src/recorder/skyramp/pdfViewerTool.ts\nvar PdfViewerTool = class {\n constructor(recorder) {\n this._pdfEmbeds = /* @__PURE__ */ new Map();\n this._pdfPageReplaced = false;\n // Prevent infinite loop when replacing full-page PDF\n this._mutationObserver = null;\n this._recorder = recorder;\n }\n install() {\n console.log(\"[PDF-Tool] Installing PDF viewer tool...\");\n this._detectAndReplacePdfEmbeds();\n this._setupAutomaticPdfDetection();\n }\n uninstall() {\n console.log(\"[PDF-Tool] Uninstalling PDF viewer tool...\");\n if (this._mutationObserver) {\n this._mutationObserver.disconnect();\n this._mutationObserver = null;\n }\n }\n cleanup() {\n console.log(\"[PDF-Tool] Cleaning up PDF viewer tool...\");\n for (const [, data] of this._pdfEmbeds.entries()) {\n data.viewer.cleanup();\n }\n this._pdfEmbeds.clear();\n if (this._mutationObserver) {\n this._mutationObserver.disconnect();\n this._mutationObserver = null;\n }\n }\n /**\n * Checks if an element is within a PDF viewer context\n */\n isWithinPdfViewer(element) {\n if (!element)\n return false;\n let current = element;\n while (current) {\n if (this._pdfEmbeds.has(current)) {\n return true;\n }\n if (current.hasAttribute && current.hasAttribute(\"data-pw-pdf-viewer\")) {\n return true;\n }\n if (current.id === \"pw-pdf-viewer-container\") {\n return true;\n }\n current = current.parentNode;\n }\n return false;\n }\n /**\n * Sets up automatic PDF detection for dynamically added content\n */\n _setupAutomaticPdfDetection() {\n console.log(\"[PDF-Tool] Setting up automatic PDF detection...\");\n this._mutationObserver = new MutationObserver((mutations) => {\n for (const mutation of mutations) {\n if (mutation.type === \"childList\" && mutation.addedNodes.length > 0) {\n setTimeout(() => {\n this._detectAndReplacePdfEmbeds();\n }, 100);\n break;\n }\n }\n });\n this._mutationObserver.observe(this._recorder.document.body, {\n childList: true,\n subtree: true\n });\n console.log(\"[PDF-Tool] \\u2705 Automatic PDF detection active\");\n }\n /**\n * Detects PDF embeds in the page and replaces them with PDF.js viewers\n */\n async _detectAndReplacePdfEmbeds() {\n console.log(\"[PDF-Tool] Scanning for PDF embeds...\");\n if (this._pdfPageReplaced) {\n console.log(\"[PDF-Tool] PDF page already replaced, skipping detection\");\n return;\n }\n const isPdfPage = this._isCurrentPagePdf();\n if (isPdfPage) {\n console.log(\"[PDF-Tool] Current page is a PDF document, replacing with PDF.js viewer...\");\n await this._replacePdfPage();\n return;\n }\n const embeds = this._recorder.document.querySelectorAll('embed[type=\"application/pdf\"], iframe[src*=\".pdf\"]');\n if (embeds.length === 0) {\n console.log(\"[PDF-Tool] No PDF embeds found\");\n return;\n }\n console.log(`[PDF-Tool] Found ${embeds.length} PDF embed(s)`);\n for (const embed of embeds) {\n await this._replacePdfEmbed(embed);\n }\n }\n /**\n * Checks if the current page itself is a PDF document\n */\n _isCurrentPagePdf() {\n const url = window.location.href;\n const doc = this._recorder.document;\n if (url.toLowerCase().endsWith(\".pdf\")) {\n console.log(\"[PDF-Tool] URL ends with .pdf:\", url);\n return true;\n }\n const fullPageEmbed = doc.querySelector('embed[type=\"application/pdf\"]');\n if (fullPageEmbed && doc.body.children.length === 1) {\n console.log(\"[PDF-Tool] Found full-page PDF embed\");\n return true;\n }\n if (url.includes(\"s3.amazonaws.com\") || url.includes(\".s3.\")) {\n console.log(\"[PDF-Tool] S3 URL detected, likely a PDF:\", url);\n return true;\n }\n return false;\n }\n /**\n * Replaces the entire page with PDF.js viewer when the page itself is a PDF\n */\n async _replacePdfPage() {\n try {\n this._pdfPageReplaced = true;\n const pdfUrl = window.location.href;\n console.log(\"[PDF-Tool] Replacing full-page PDF with PDF.js viewer:\", pdfUrl.substring(0, 100));\n const doc = this._recorder.document;\n doc.body.innerHTML = \"\";\n const container = doc.createElement(\"div\");\n container.setAttribute(\"data-pw-pdf-viewer\", \"true\");\n container.id = \"pw-pdf-viewer-container\";\n container.style.cssText = `\n position: fixed;\n top: 0;\n left: 0;\n width: 100%;\n height: 100%;\n z-index: 2147483647;\n background: #525252;\n `;\n doc.body.appendChild(container);\n const viewer = new PdfJsViewer(container);\n const success = await PdfViewerHelper.renderPdfWithPdfJs({\n pdfUrl,\n containerElement: container,\n onSuccess: () => {\n console.log(\"[PDF-Tool] \\u2705 Full-page PDF replaced with PDF.js viewer\");\n },\n onError: (error) => {\n console.error(\"[PDF-Tool] \\u274C Failed to render full-page PDF:\", error);\n }\n });\n if (!success) {\n console.error(\"[PDF-Tool] Failed to render full-page PDF\");\n }\n } catch (error) {\n console.error(\"[PDF-Tool] Error replacing full-page PDF:\", error);\n }\n }\n /**\n * Replaces a single PDF embed with PDF.js viewer\n */\n async _replacePdfEmbed(embed) {\n try {\n if (this._pdfEmbeds.has(embed)) {\n console.log(\"[PDF-Tool] PDF embed already replaced, skipping...\");\n return;\n }\n let pdfUrl = embed.getAttribute(\"src\");\n if (!pdfUrl || pdfUrl === \"about:blank\") {\n console.log('[PDF-Tool] Embed has src=\"about:blank\", using page URL as PDF URL...');\n pdfUrl = window.location.href;\n if (!pdfUrl.includes(\".pdf\")) {\n console.log(\"[PDF-Tool] Page URL does not appear to be a PDF, skipping\");\n return;\n }\n console.log(\"[PDF-Tool] Using page URL as PDF:\", pdfUrl.substring(0, 100));\n }\n console.log(\"[PDF-Tool] Replacing PDF embed with PDF.js viewer:\", pdfUrl.substring(0, 100));\n const container = this._recorder.document.createElement(\"div\");\n container.setAttribute(\"data-pw-pdf-viewer\", \"true\");\n container.style.cssText = `\n position: absolute;\n top: ${embed.offsetTop}px;\n left: ${embed.offsetLeft}px;\n width: ${embed.offsetWidth || 800}px;\n height: ${embed.offsetHeight || 600}px;\n z-index: 2147483647;\n background: #525252;\n `;\n const parent = embed.parentNode;\n if (!parent) return;\n parent.insertBefore(container, embed);\n embed.style.display = \"none\";\n const viewer = new PdfJsViewer(container);\n this._pdfEmbeds.set(embed, { originalParent: parent, viewer, container });\n await PdfViewerHelper.renderPdfWithPdfJs({\n pdfUrl,\n containerElement: container,\n onSuccess: () => {\n console.log(\"[PDF-Tool] \\u2705 PDF embed replaced successfully\");\n },\n onError: (error) => {\n console.error(\"[PDF-Tool] \\u274C Failed to render PDF:\", error);\n embed.style.display = \"\";\n if (container.parentNode) {\n container.parentNode.removeChild(container);\n }\n this._pdfEmbeds.delete(embed);\n }\n });\n } catch (error) {\n console.error(\"[PDF-Tool] Error replacing PDF embed:\", error);\n }\n }\n};\n\n// packages/injected/src/recorder/skyramp/dragDropTool.ts\nfunction addEventListener(target, eventName, listener, options) {\n target.addEventListener(eventName, listener, options);\n const remove = () => {\n target.removeEventListener(eventName, listener, options);\n };\n return remove;\n}\nfunction removeEventListeners(listeners) {\n for (const listener of listeners)\n listener();\n listeners.splice(0, listeners.length);\n}\nfunction getTimestamp(recorder) {\n return recorder.injectedScript.utils.builtins.Date.now().toString();\n}\nvar _DragDropTool = class _DragDropTool {\n constructor(recorder) {\n this._dragState = null;\n this._listeners = [];\n this._lastClickTime = 0;\n this._lastClickTimeout = null;\n this._lastMousePosition = null;\n this._wheelToggleTimeout = null;\n this._isWheelSequence = false;\n this._wheelAccumulator = null;\n this._wheelDebounceTimeout = null;\n // PDF viewer tool (activated when PDF embeds are detected)\n this._pdfViewerTool = null;\n // Cleanup callbacks for always-on GoJS diagram listeners\n this._goJSAlwaysOnRemovers = [];\n this._recorder = recorder;\n }\n cursor() {\n return \"grab\";\n }\n install() {\n this._arm();\n this._checkAndActivatePdfViewer();\n this._hookGoJSDiagramsAlwaysOn();\n }\n uninstall() {\n if (this._lastClickTimeout) {\n clearTimeout(this._lastClickTimeout);\n this._lastClickTimeout = null;\n }\n if (this._wheelToggleTimeout) {\n clearTimeout(this._wheelToggleTimeout);\n this._wheelToggleTimeout = null;\n }\n this._flushWheelAction();\n if (this._wheelDebounceTimeout) {\n clearTimeout(this._wheelDebounceTimeout);\n this._wheelDebounceTimeout = null;\n }\n if (this._dragState && this._dragState.source && this._dragState.target) {\n this._capture();\n }\n for (const remove of this._goJSAlwaysOnRemovers)\n remove();\n this._goJSAlwaysOnRemovers = [];\n delete this._recorder.document.__skyrampGoJSHooked;\n this._disarm();\n }\n cleanup() {\n if (this._lastClickTimeout) {\n clearTimeout(this._lastClickTimeout);\n this._lastClickTimeout = null;\n }\n if (this._wheelToggleTimeout) {\n clearTimeout(this._wheelToggleTimeout);\n this._wheelToggleTimeout = null;\n }\n this._flushWheelAction();\n if (this._wheelDebounceTimeout) {\n clearTimeout(this._wheelDebounceTimeout);\n this._wheelDebounceTimeout = null;\n }\n if (this._dragState && this._dragState.source && this._dragState.target) {\n this._capture();\n }\n if (this._listeners.length > 0) {\n this._disarm();\n this._arm();\n }\n }\n onDblClick(event) {\n if (this._lastClickTimeout) {\n clearTimeout(this._lastClickTimeout);\n this._lastClickTimeout = null;\n }\n const target = event.target;\n if (!target || this._isPlaywrightElement(target))\n return;\n event.preventDefault();\n try {\n const action = {\n name: \"click\",\n selector: \"body\",\n button: \"left\",\n modifiers: 0,\n clickCount: 2,\n // Double-click\n position: {\n x: Math.round(event.clientX),\n y: Math.round(event.clientY)\n },\n signals: [],\n timestamp: getTimestamp(this._recorder)\n };\n this._recorder.recordAction(action);\n this._deactivate();\n } catch (error) {\n console.error(\"[PW-RECORDER] Error recording position-based double-click:\", error);\n this._deactivate();\n }\n }\n _flushWheelAction() {\n if (!this._wheelAccumulator)\n return;\n const absX = Math.abs(this._wheelAccumulator.deltaX);\n const absY = Math.abs(this._wheelAccumulator.deltaY);\n if (absX < _DragDropTool.WHEEL_NOISE_AXIS_THRESHOLD && absY < _DragDropTool.WHEEL_NOISE_AXIS_THRESHOLD) {\n console.log(\"[PW-RECORDER] Suppressing Magic Mouse noise wheel:\", {\n deltaX: this._wheelAccumulator.deltaX,\n deltaY: this._wheelAccumulator.deltaY,\n accumulatedFor: Date.now() - this._wheelAccumulator.startTime + \"ms\"\n });\n this._wheelAccumulator = null;\n return;\n }\n try {\n const action = {\n name: \"mouse.wheel\",\n position: this._wheelAccumulator.position,\n deltaX: this._wheelAccumulator.deltaX,\n deltaY: this._wheelAccumulator.deltaY,\n deltaZ: this._wheelAccumulator.deltaZ,\n modifiers: this._wheelAccumulator.modifiers,\n signals: [],\n timestamp: getTimestamp(this._recorder)\n };\n this._recorder.recordAction(action);\n console.log(\"[PW-RECORDER] Flushed accumulated wheel action:\", {\n deltaX: action.deltaX,\n deltaY: action.deltaY,\n deltaZ: action.deltaZ,\n accumulatedFor: Date.now() - this._wheelAccumulator.startTime + \"ms\"\n });\n } catch (error) {\n console.error(\"[PW-RECORDER] Error flushing wheel action:\", error);\n }\n this._wheelAccumulator = null;\n }\n _arm() {\n var _a;\n this._dragState = {\n source: null,\n target: null,\n sourcePoint: null,\n targetPoint: null,\n startTime: Date.now(),\n isCanvas: false,\n isGoJS: false,\n isReactFlow: false,\n isSlider: false,\n dropDetected: false,\n captured: false\n };\n (_a = this._recorder.injectedScript.document.body) == null ? void 0 : _a.setAttribute(\"data-pw-cursor\", \"grab\");\n const onDragStart = (e) => {\n const dragEvent = e;\n if (this._dragState) {\n const sourceElement = this._recorder.document.elementFromPoint(dragEvent.clientX, dragEvent.clientY);\n if (sourceElement && !this._isPlaywrightElement(sourceElement)) {\n this._dragState.source = this._selectDraggableAncestor(sourceElement);\n this._dragState.sourcePoint = { x: dragEvent.clientX, y: dragEvent.clientY };\n } else {\n }\n }\n };\n const onDragOver = (e) => {\n const dragEvent = e;\n if (this._dragState && this._dragState.source) {\n const targetElement = this._recorder.document.elementFromPoint(dragEvent.clientX, dragEvent.clientY);\n if (targetElement && !this._isPlaywrightElement(targetElement)) {\n const potentialTarget = this._selectDroppableAncestor(targetElement);\n if (potentialTarget !== this._dragState.source) {\n this._dragState.target = potentialTarget;\n this._dragState.targetPoint = { x: dragEvent.clientX, y: dragEvent.clientY };\n }\n }\n }\n };\n const onPointerDown = (e) => {\n const pointerEvent = e;\n if (this._dragState) {\n this._dragState.source = null;\n this._dragState.target = null;\n this._dragState.sourcePoint = null;\n this._dragState.targetPoint = null;\n this._dragState.isCanvas = false;\n this._dragState.isGoJS = false;\n this._dragState.isReactFlow = false;\n this._dragState.isSlider = false;\n this._dragState.dropDetected = false;\n this._dragState.startTime = Date.now();\n }\n if (this._dragState) {\n const sourceElement = this._recorder.document.elementFromPoint(pointerEvent.clientX, pointerEvent.clientY);\n if (sourceElement && !this._isPlaywrightElement(sourceElement)) {\n if (this._isReactFlowElement(sourceElement)) {\n this._dragState.isReactFlow = true;\n let reactFlowContainer = sourceElement;\n while (reactFlowContainer && !reactFlowContainer.classList.contains(\"react-flow\")) {\n reactFlowContainer = reactFlowContainer.parentElement;\n }\n this._dragState.source = reactFlowContainer || sourceElement;\n this._dragState.sourcePoint = { x: pointerEvent.clientX, y: pointerEvent.clientY };\n } else if (this._isGoJSElement(sourceElement)) {\n this._dragState.isGoJS = true;\n this._dragState.source = sourceElement;\n this._dragState.sourcePoint = { x: pointerEvent.clientX, y: pointerEvent.clientY };\n } else if (this._isCanvasElement(sourceElement)) {\n this._dragState.isCanvas = true;\n this._dragState.source = sourceElement;\n this._dragState.sourcePoint = { x: pointerEvent.clientX, y: pointerEvent.clientY };\n } else {\n const draggableElement = this._selectDraggableAncestor(sourceElement);\n if (draggableElement !== sourceElement) {\n this._dragState.source = draggableElement;\n this._dragState.sourcePoint = { x: pointerEvent.clientX, y: pointerEvent.clientY };\n } else {\n const sliderThumb = this._findSliderThumb(sourceElement);\n if (sliderThumb) {\n this._dragState.isSlider = true;\n this._dragState.source = sliderThumb;\n this._dragState.sourcePoint = { x: pointerEvent.clientX, y: pointerEvent.clientY };\n } else {\n this._dragState.source = sourceElement;\n this._dragState.sourcePoint = { x: pointerEvent.clientX, y: pointerEvent.clientY };\n }\n }\n }\n }\n }\n };\n const onPointerMove = (e) => {\n const pointerEvent = e;\n if (this._dragState && this._dragState.source && pointerEvent.buttons === 1) {\n const targetElement = this._recorder.document.elementFromPoint(pointerEvent.clientX, pointerEvent.clientY);\n if (targetElement && !this._isPlaywrightElement(targetElement)) {\n if (this._dragState.isReactFlow && this._isReactFlowElement(targetElement)) {\n this._dragState.target = this._dragState.source;\n this._dragState.targetPoint = { x: pointerEvent.clientX, y: pointerEvent.clientY };\n } else if (this._dragState.isGoJS && this._isCanvasElement(targetElement)) {\n this._dragState.target = targetElement;\n this._dragState.targetPoint = { x: pointerEvent.clientX, y: pointerEvent.clientY };\n } else if (this._dragState.isCanvas && this._isCanvasElement(targetElement)) {\n this._dragState.target = targetElement;\n this._dragState.targetPoint = { x: pointerEvent.clientX, y: pointerEvent.clientY };\n } else if (this._dragState.isSlider) {\n this._dragState.target = this._dragState.source;\n this._dragState.targetPoint = { x: pointerEvent.clientX, y: pointerEvent.clientY };\n } else if (!this._dragState.isCanvas && !this._dragState.isReactFlow && !this._dragState.isSlider) {\n const potentialTarget = this._selectDroppableAncestor(targetElement);\n if (potentialTarget !== this._dragState.source) {\n this._dragState.target = potentialTarget;\n this._dragState.targetPoint = { x: pointerEvent.clientX, y: pointerEvent.clientY };\n } else if (targetElement !== this._dragState.source) {\n this._dragState.target = targetElement;\n this._dragState.targetPoint = { x: pointerEvent.clientX, y: pointerEvent.clientY };\n }\n }\n }\n }\n };\n const onPointerUp = (e) => {\n const pointerEvent = e;\n if (this._dragState && this._dragState.source && this._dragState.sourcePoint) {\n if (this._dragState.target) {\n const sourceColumn = this._getSourceColumn(this._dragState.source);\n const targetColumn = this._dragState.target;\n this._capture();\n } else {\n const distance = Math.sqrt(\n Math.pow(pointerEvent.clientX - this._dragState.sourcePoint.x, 2) + Math.pow(pointerEvent.clientY - this._dragState.sourcePoint.y, 2)\n );\n if (distance >= 5) {\n this._deactivate();\n return;\n }\n if (distance < 5) {\n if (pointerEvent.button === 2) {\n return;\n }\n if (this._lastClickTimeout) {\n clearTimeout(this._lastClickTimeout);\n }\n this._lastClickTimeout = setTimeout(() => {\n try {\n const action = {\n name: \"click\",\n selector: \"body\",\n button: \"left\",\n modifiers: 0,\n clickCount: 1,\n position: {\n x: Math.round(pointerEvent.clientX),\n y: Math.round(pointerEvent.clientY)\n },\n signals: [],\n timestamp: getTimestamp(this._recorder)\n };\n this._recorder.recordAction(action);\n this._deactivate();\n } catch (error) {\n console.error(\"[PW-RECORDER] Error recording position-based click:\", error);\n this._deactivate();\n }\n this._lastClickTimeout = null;\n }, 300);\n }\n }\n } else {\n console.log(\"[PW-RECORDER] PointerUp - no valid drag state\");\n }\n };\n const onDrop = (e) => {\n const dragEvent = e;\n if (this._dragState && this._dragState.source) {\n this._dragState.dropDetected = true;\n if (!this._dragState.target) {\n const targetElement = this._recorder.document.elementFromPoint(dragEvent.clientX, dragEvent.clientY);\n if (targetElement && !this._isPlaywrightElement(targetElement)) {\n this._dragState.target = this._selectDroppableAncestor(targetElement);\n this._dragState.targetPoint = { x: dragEvent.clientX, y: dragEvent.clientY };\n }\n }\n if (this._dragState.target) {\n const sourceColumn = this._getSourceColumn(this._dragState.source);\n const targetColumn = this._dragState.target;\n this._capture();\n } else {\n this._deactivate();\n }\n } else {\n this._deactivate();\n }\n };\n const onDragEnd = (e) => {\n var _a2;\n const dragEvent = e;\n if (this._dragState && this._dragState.source) {\n if (this._dragState.dropDetected) {\n if (!this._dragState.target) {\n const targetElement = this._recorder.document.elementFromPoint(dragEvent.clientX, dragEvent.clientY);\n if (targetElement && !this._isPlaywrightElement(targetElement)) {\n this._dragState.target = this._selectDroppableAncestor(targetElement);\n this._dragState.targetPoint = { x: dragEvent.clientX, y: dragEvent.clientY };\n }\n }\n if (this._dragState.target) {\n const sourceColumn = this._getSourceColumn(this._dragState.source);\n const targetColumn = this._dragState.target;\n this._capture();\n }\n } else {\n const dropEffect = (_a2 = dragEvent.dataTransfer) == null ? void 0 : _a2.dropEffect;\n if (dropEffect && dropEffect !== \"none\") {\n this._deactivate();\n return;\n }\n console.log(\"[PW-RECORDER] Drag cancelled (no drop event), not recording\");\n }\n this._dragState = {\n source: null,\n target: null,\n sourcePoint: null,\n targetPoint: null,\n startTime: Date.now(),\n isCanvas: false,\n isGoJS: false,\n isReactFlow: false,\n isSlider: false,\n dropDetected: false,\n captured: false\n };\n }\n };\n const onWheel = (e) => {\n var _a2;\n const wheelEvent = e;\n const target = wheelEvent.target;\n if (!target || this._isPlaywrightElement(target))\n return;\n if (this._isGoJSElement(target))\n return;\n const isInPdfViewer = ((_a2 = this._pdfViewerTool) == null ? void 0 : _a2.isWithinPdfViewer(target)) || false;\n if (wheelEvent.ctrlKey && !isInPdfViewer) {\n e.preventDefault();\n }\n let modifiers = 0;\n if (wheelEvent.altKey)\n modifiers |= 1;\n if (wheelEvent.ctrlKey)\n modifiers |= 2;\n if (wheelEvent.metaKey)\n modifiers |= 4;\n if (wheelEvent.shiftKey)\n modifiers |= 8;\n try {\n const currentPosition = {\n x: Math.round(wheelEvent.clientX),\n y: Math.round(wheelEvent.clientY)\n };\n if (!this._isWheelSequence) {\n this._isWheelSequence = true;\n const commentAction = {\n name: \"comment\",\n text: \"Mouse scrolling block\",\n signals: [],\n timestamp: getTimestamp(this._recorder)\n };\n this._recorder.recordAction(commentAction);\n const timeoutAction = {\n name: \"waitForTimeout\",\n duration: _DragDropTool.WHEEL_SCROLL_TIMEOUT_MS,\n signals: [],\n timestamp: getTimestamp(this._recorder)\n };\n this._recorder.recordAction(timeoutAction);\n }\n if (!this._lastMousePosition || this._lastMousePosition.x !== currentPosition.x || this._lastMousePosition.y !== currentPosition.y) {\n const mouseMoveAction = {\n name: \"mouse.move\",\n position: currentPosition,\n signals: [],\n timestamp: getTimestamp(this._recorder)\n };\n this._recorder.recordAction(mouseMoveAction);\n this._lastMousePosition = currentPosition;\n }\n const now = Date.now();\n if (!this._wheelAccumulator) {\n this._wheelAccumulator = {\n deltaX: wheelEvent.deltaX,\n deltaY: wheelEvent.deltaY,\n deltaZ: wheelEvent.deltaZ,\n position: currentPosition,\n modifiers,\n startTime: now\n };\n } else {\n this._wheelAccumulator.deltaX += wheelEvent.deltaX;\n this._wheelAccumulator.deltaY += wheelEvent.deltaY;\n this._wheelAccumulator.deltaZ += wheelEvent.deltaZ;\n this._wheelAccumulator.position = currentPosition;\n this._wheelAccumulator.modifiers = modifiers;\n if (now - this._wheelAccumulator.startTime > _DragDropTool.WHEEL_MAX_ACCUMULATION_MS) {\n this._flushWheelAction();\n this._wheelAccumulator = {\n deltaX: wheelEvent.deltaX,\n deltaY: wheelEvent.deltaY,\n deltaZ: wheelEvent.deltaZ,\n position: currentPosition,\n modifiers,\n startTime: now\n };\n }\n }\n if (this._wheelDebounceTimeout) {\n clearTimeout(this._wheelDebounceTimeout);\n }\n this._wheelDebounceTimeout = setTimeout(() => {\n this._flushWheelAction();\n this._wheelDebounceTimeout = null;\n }, _DragDropTool.WHEEL_DEBOUNCE_MS);\n if (this._wheelToggleTimeout) {\n clearTimeout(this._wheelToggleTimeout);\n }\n this._wheelToggleTimeout = setTimeout(() => {\n this._deactivate();\n }, _DragDropTool.WHEEL_TOOL_DISABLE_MS);\n } catch (error) {\n console.error(\"[PW-RECORDER] Error recording wheel event:\", error);\n }\n };\n const onContextMenu = (e) => {\n const contextEvent = e;\n const target = contextEvent.target;\n if (!target || this._isPlaywrightElement(target))\n return;\n e.preventDefault();\n try {\n this._lastMousePosition = { x: Math.round(contextEvent.clientX), y: Math.round(contextEvent.clientY) };\n const action = {\n name: \"click\",\n selector: \"body\",\n button: \"right\",\n modifiers: 0,\n clickCount: 1,\n position: {\n x: Math.round(contextEvent.clientX),\n y: Math.round(contextEvent.clientY)\n },\n signals: [],\n timestamp: getTimestamp(this._recorder)\n };\n this._recorder.recordAction(action);\n this._deactivate();\n } catch (error) {\n console.error(\"[PW-RECORDER] Error recording right-click:\", error);\n this._deactivate();\n }\n };\n this._listeners = [\n addEventListener(this._recorder.document, \"dragstart\", onDragStart, true),\n addEventListener(this._recorder.document, \"dragover\", onDragOver, true),\n addEventListener(this._recorder.document, \"drop\", onDrop, true),\n addEventListener(this._recorder.document, \"dragend\", onDragEnd, true),\n addEventListener(this._recorder.document, \"pointerdown\", onPointerDown, true),\n addEventListener(this._recorder.document, \"pointermove\", onPointerMove, true),\n addEventListener(this._recorder.document, \"pointerup\", onPointerUp, true),\n addEventListener(this._recorder.document, \"wheel\", onWheel, { passive: false, capture: true }),\n addEventListener(this._recorder.document, \"contextmenu\", onContextMenu, true)\n ];\n }\n _disarm() {\n removeEventListeners(this._listeners);\n this._listeners = [];\n this._dragState = null;\n }\n // Deselect the DD tool after a completed (or aborted) drag interaction.\n // setMode() alone is not enough: in deeply-nested iframes the broadcast\n // round-trip is slow enough that the next user input gets captured by\n // this tool's still-armed listeners. Disarm locally first, then signal\n // the mode change for the rest of the recorder to follow.\n _deactivate() {\n if (this._lastClickTimeout) {\n clearTimeout(this._lastClickTimeout);\n this._lastClickTimeout = null;\n }\n if (this._wheelToggleTimeout) {\n clearTimeout(this._wheelToggleTimeout);\n this._wheelToggleTimeout = null;\n }\n if (this._wheelDebounceTimeout) {\n clearTimeout(this._wheelDebounceTimeout);\n this._wheelDebounceTimeout = null;\n }\n this._isWheelSequence = false;\n this._wheelAccumulator = null;\n this._disarm();\n if (this._recorder.state.mode === \"recordingDrag\")\n this._recorder.setMode(\"recording\");\n }\n /**\n * Checks if there are PDF embeds on the page and activates PDF viewer tool if needed\n */\n _checkAndActivatePdfViewer() {\n const doc = this._recorder.document;\n if (window.__pwPdfViewerInstalled) {\n console.log(\"[DD-Tool] PDF viewer already installed, skipping\");\n return;\n }\n const hasPdfEmbed = doc.querySelector('embed[type=\"application/pdf\"], iframe[src*=\".pdf\"]');\n const isPdfPage = window.location.href.toLowerCase().endsWith(\".pdf\") || window.location.href.includes(\"s3.amazonaws.com\") || window.location.href.includes(\".s3.\");\n if (hasPdfEmbed || isPdfPage) {\n console.log(\"[DD-Tool] PDF detected, activating PDF viewer tool...\");\n this._pdfViewerTool = new PdfViewerTool(this._recorder);\n this._pdfViewerTool.install();\n window.__pwPdfViewerInstalled = true;\n console.log(\"[DD-Tool] \\u2705 PDF viewer tool activated (permanent)\");\n }\n }\n _getActualPageElement(composedPath) {\n for (const target of composedPath) {\n const element = target;\n if (element && element.nodeType === Node.ELEMENT_NODE && !this._isPlaywrightElement(element)) {\n return element;\n }\n }\n return null;\n }\n _isPlaywrightElement(element) {\n var _a;\n const nodeName = ((_a = element.nodeName) == null ? void 0 : _a.toLowerCase()) || \"\";\n const id = element.id || \"\";\n const isPlaywright = nodeName.startsWith(\"x-pw-\") || id === \"x-pw-glass\" || element.classList.contains(\"playwright-overlay\") || element.hasAttribute(\"data-playwright\");\n return isPlaywright;\n }\n _isCanvasElement(element) {\n var _a;\n return ((_a = element.tagName) == null ? void 0 : _a.toLowerCase()) === \"canvas\";\n }\n _isGoJSElement(element) {\n var _a, _b;\n if (((_a = element.tagName) == null ? void 0 : _a.toLowerCase()) !== \"canvas\")\n return false;\n const win = (_b = element.ownerDocument) == null ? void 0 : _b.defaultView;\n return !!((win == null ? void 0 : win.myDiagram) || (win == null ? void 0 : win.myPalette));\n }\n /**\n * Walks up from a canvas element to find the GoJS Diagram/Palette that owns it,\n * using the generic go.Diagram.fromDiv() API (works for any GoJS application).\n * Returns the diagram instance, whether it is a Palette, and a CSS selector\n * for the container div derived from the element's actual DOM attributes.\n */\n _findGoJSContainer(canvas) {\n var _a, _b, _c;\n const win = (_a = canvas.ownerDocument) == null ? void 0 : _a.defaultView;\n if (!((_c = (_b = win == null ? void 0 : win.go) == null ? void 0 : _b.Diagram) == null ? void 0 : _c.fromDiv))\n return null;\n let el = canvas.parentElement;\n while (el && el !== canvas.ownerDocument.body) {\n const diagram = win.go.Diagram.fromDiv(el);\n if (diagram) {\n const isPalette = !!(win.go.Palette && diagram instanceof win.go.Palette);\n let containerSelector;\n if (el.id) {\n containerSelector = `#${el.id}`;\n } else if (el.getAttribute(\"data-testid\")) {\n containerSelector = `[data-testid=\"${el.getAttribute(\"data-testid\")}\"]`;\n } else {\n const parent = el.parentElement;\n if (parent) {\n const idx = Array.from(parent.children).indexOf(el) + 1;\n containerSelector = `${el.tagName.toLowerCase()}:nth-child(${idx})`;\n } else {\n containerSelector = el.tagName.toLowerCase();\n }\n }\n return { diagram, isPalette, containerSelector };\n }\n el = el.parentElement;\n }\n return null;\n }\n /**\n * Build a stable CSS selector from a DOM element — same logic as gojsLinkTool._buildSelector.\n */\n _buildSelectorFromEl(el) {\n if (el.id)\n return `#${el.id}`;\n if (el.getAttribute(\"data-testid\"))\n return `[data-testid=\"${el.getAttribute(\"data-testid\")}\"]`;\n const parent = el.parentElement;\n if (parent) {\n const idx = Array.from(parent.children).indexOf(el) + 1;\n return `${el.tagName.toLowerCase()}:nth-child(${idx})`;\n }\n return el.tagName.toLowerCase();\n }\n /**\n * Emit a diagramNodeAdd action for a GoJS palette drop, with full anchor computation.\n * Mirrors gojsLinkTool._emitDiagramNodeAdd so normal recording mode produces the\n * same JSONL as gojsLinkTool mode.\n */\n _emitGoJSNodeAdd(diagram, sourcePanelSelector, targetPanelSelector, category, key, docX, docY) {\n let anchorKey;\n let anchorOffsetX;\n let anchorOffsetY;\n let anchorDocX;\n let anchorDocY;\n let minDist = Infinity;\n diagram.nodes.each((node) => {\n var _a;\n if (!(node == null ? void 0 : node.data)) return;\n const nKey = String((_a = node.data.key) != null ? _a : \"\");\n if (!nKey || nKey === key) return;\n const dx = node.location.x - docX;\n const dy = node.location.y - docY;\n const dist = Math.sqrt(dx * dx + dy * dy);\n if (dist < minDist) {\n minDist = dist;\n anchorKey = nKey;\n anchorOffsetX = Math.round(docX - node.location.x);\n anchorOffsetY = Math.round(docY - node.location.y);\n anchorDocX = Math.round(node.location.x);\n anchorDocY = Math.round(node.location.y);\n }\n });\n const action = {\n name: \"diagramNodeAdd\",\n diagramType: \"gojs\",\n sourcePanelSelector,\n targetPanelSelector,\n sourceIsPalette: true,\n targetIsPalette: false,\n sourceCategory: category,\n sourceKey: key,\n targetDocX: Math.round(docX),\n targetDocY: Math.round(docY),\n anchorKey,\n anchorOffsetX,\n anchorOffsetY,\n anchorDocX,\n anchorDocY,\n signals: [],\n timestamp: getTimestamp(this._recorder)\n };\n this._recorder.recordAction(action);\n this._deactivate();\n }\n /**\n * Emit a diagramLinkAdd action for a GoJS link drawn in normal recording mode.\n * Mirrors gojsLinkTool._emitDiagramLinkAdd.\n */\n _emitGoJSLinkAdd(fromKey, toKey, fromPort, toPort, panelSelector) {\n const action = {\n name: \"diagramLinkAdd\",\n diagramType: \"gojs\",\n panelSelector,\n fromKey,\n toKey,\n fromPort,\n toPort,\n signals: [],\n timestamp: getTimestamp(this._recorder)\n };\n this._recorder.recordAction(action);\n this._deactivate();\n }\n /**\n * Register always-on GoJS diagram listeners for ExternalObjectsDropped and LinkDrawn.\n * Called from install() so GoJS events are captured even in normal recording mode.\n *\n * Guards:\n * - __skyrampGoJSHooked: prevents double-registration on the same document.\n * - __skyrampGojsLinkToolActive: always-on handlers skip emission when gojsLinkTool\n * is active (it handles the same events with higher fidelity).\n */\n _hookGoJSDiagramsAlwaysOn() {\n var _a, _b;\n const doc = this._recorder.document;\n if (doc.__skyrampGoJSHooked)\n return;\n const win = doc.defaultView;\n if (!((_b = (_a = win == null ? void 0 : win.go) == null ? void 0 : _a.Diagram) == null ? void 0 : _b.fromDiv))\n return;\n const canvases = Array.from(doc.querySelectorAll(\"canvas\"));\n if (!canvases.length)\n return;\n const hookedDiagrams = /* @__PURE__ */ new Set();\n for (const canvas of canvases) {\n let el = canvas.parentElement;\n while (el && el !== doc.body) {\n const diagram = win.go.Diagram.fromDiv(el);\n if (diagram) {\n const isPalette = !!(win.go.Palette && diagram instanceof win.go.Palette);\n if (!isPalette && !hookedDiagrams.has(diagram)) {\n hookedDiagrams.add(diagram);\n const panelSelector = this._buildSelectorFromEl(el);\n const externalDropHandler = (e) => {\n if (doc.__skyrampGojsLinkToolActive) return;\n e.subject.each((part) => {\n var _a2, _b2;\n if (!(part == null ? void 0 : part.data)) return;\n if (part.data.from !== void 0) return;\n const category = String((_a2 = part.data.category) != null ? _a2 : \"\");\n const key = String((_b2 = part.data.key) != null ? _b2 : \"\");\n const loc = part.location;\n let sourcePanelSelector = \"\";\n try {\n const palCanvases = Array.from(doc.querySelectorAll(\"canvas\"));\n for (const pc of palCanvases) {\n let pel = pc.parentElement;\n while (pel && pel !== doc.body) {\n const pd = win.go.Diagram.fromDiv(pel);\n if (pd && win.go.Palette && pd instanceof win.go.Palette) {\n sourcePanelSelector = this._buildSelectorFromEl(pel);\n break;\n }\n pel = pel.parentElement;\n }\n if (sourcePanelSelector) break;\n }\n } catch (_) {\n }\n setTimeout(() => {\n var _a3, _b3, _c;\n const finalLoc = (_a3 = part.location) != null ? _a3 : loc;\n this._emitGoJSNodeAdd(diagram, sourcePanelSelector, panelSelector, category, key, (_b3 = finalLoc == null ? void 0 : finalLoc.x) != null ? _b3 : 0, (_c = finalLoc == null ? void 0 : finalLoc.y) != null ? _c : 0);\n }, 0);\n });\n };\n const linkDrawnHandler = (e) => {\n var _a2, _b2, _c, _d;\n if (doc.__skyrampGojsLinkToolActive) return;\n const link = e.subject;\n if (!(link == null ? void 0 : link.data)) return;\n const fromKey = String((_a2 = link.data.from) != null ? _a2 : \"\");\n const toKey = String((_b2 = link.data.to) != null ? _b2 : \"\");\n if (!fromKey || !toKey) return;\n this._emitGoJSLinkAdd(\n fromKey,\n toKey,\n String((_c = link.data.fromPort) != null ? _c : \"\"),\n String((_d = link.data.toPort) != null ? _d : \"\"),\n panelSelector\n );\n };\n diagram.addDiagramListener(\"ExternalObjectsDropped\", externalDropHandler);\n diagram.addDiagramListener(\"LinkDrawn\", linkDrawnHandler);\n this._goJSAlwaysOnRemovers.push(() => {\n try {\n diagram.removeDiagramListener(\"ExternalObjectsDropped\", externalDropHandler);\n diagram.removeDiagramListener(\"LinkDrawn\", linkDrawnHandler);\n } catch (_) {\n }\n });\n }\n break;\n }\n el = el.parentElement;\n }\n }\n if (hookedDiagrams.size > 0) {\n doc.__skyrampGoJSHooked = true;\n console.log(\"[DragDropTool] always-on GoJS listeners registered on\", hookedDiagrams.size, \"diagram(s)\");\n }\n }\n _captureGoJSDrag() {\n var _a, _b, _c, _d, _e, _f, _g, _h, _i, _j, _k;\n if (!((_a = this._dragState) == null ? void 0 : _a.source) || !((_b = this._dragState) == null ? void 0 : _b.target) || !((_c = this._dragState) == null ? void 0 : _c.sourcePoint) || !((_d = this._dragState) == null ? void 0 : _d.targetPoint)) {\n return;\n }\n try {\n const srcCanvas = this._dragState.source;\n const dstCanvas = this._dragState.target;\n const win = (_e = srcCanvas.ownerDocument) == null ? void 0 : _e.defaultView;\n const srcInfo = this._findGoJSContainer(srcCanvas);\n const dstInfo = this._findGoJSContainer(dstCanvas);\n const sourcePanelSelector = (_f = srcInfo == null ? void 0 : srcInfo.containerSelector) != null ? _f : \"\";\n const targetPanelSelector = (_g = dstInfo == null ? void 0 : dstInfo.containerSelector) != null ? _g : \"\";\n const sourceIsPalette = (_h = srcInfo == null ? void 0 : srcInfo.isPalette) != null ? _h : false;\n const targetIsPalette = (_i = dstInfo == null ? void 0 : dstInfo.isPalette) != null ? _i : false;\n let sourceCategory = \"\";\n let sourceKey = \"\";\n const srcDiagram = srcInfo == null ? void 0 : srcInfo.diagram;\n if (srcDiagram && (win == null ? void 0 : win.go)) {\n const srcRect = srcCanvas.getBoundingClientRect();\n const vpX = this._dragState.sourcePoint.x - srcRect.left;\n const vpY = this._dragState.sourcePoint.y - srcRect.top;\n try {\n const docPt = srcDiagram.transformViewToDoc(new win.go.Point(vpX, vpY));\n const part = srcDiagram.findPartAt(docPt, false);\n if (part == null ? void 0 : part.data) {\n sourceCategory = (_j = part.data.category) != null ? _j : \"\";\n sourceKey = String((_k = part.data.key) != null ? _k : \"\");\n }\n } catch (_e2) {\n }\n }\n let targetDocX = 0;\n let targetDocY = 0;\n const dstDiagram = dstInfo == null ? void 0 : dstInfo.diagram;\n if (dstDiagram && (win == null ? void 0 : win.go)) {\n const dstRect = dstCanvas.getBoundingClientRect();\n const vpX = this._dragState.targetPoint.x - dstRect.left;\n const vpY = this._dragState.targetPoint.y - dstRect.top;\n try {\n const docPt = dstDiagram.transformViewToDoc(new win.go.Point(vpX, vpY));\n targetDocX = Math.round(docPt.x);\n targetDocY = Math.round(docPt.y);\n } catch (_e2) {\n }\n }\n if (!sourceIsPalette && sourceKey === \"\") {\n this._deactivate();\n return;\n }\n const doc = srcCanvas.ownerDocument;\n if (sourceIsPalette && doc.__skyrampGoJSHooked) {\n this._deactivate();\n return;\n }\n const action = {\n name: \"diagramNodeAdd\",\n diagramType: \"gojs\",\n sourcePanelSelector,\n targetPanelSelector,\n sourceIsPalette,\n targetIsPalette,\n sourceCategory,\n sourceKey,\n targetDocX,\n targetDocY,\n signals: [],\n timestamp: getTimestamp(this._recorder)\n };\n this._recorder.recordAction(action);\n this._deactivate();\n } catch (error) {\n console.error(\"[PW-RECORDER] Error capturing GoJS drag:\", error);\n this._captureCanvasDrag();\n return;\n }\n this._dragState = {\n source: null,\n target: null,\n sourcePoint: null,\n targetPoint: null,\n startTime: Date.now(),\n isCanvas: false,\n isGoJS: false,\n isReactFlow: false,\n isSlider: false,\n dropDetected: false,\n captured: false\n };\n }\n _isReactFlowElement(element) {\n let current = element;\n while (current) {\n const classList = Array.from(current.classList || []);\n if (classList.includes(\"react-flow\") || classList.includes(\"react-flow__pane\") || classList.includes(\"react-flow__viewport\")) {\n return true;\n }\n current = current.parentElement;\n }\n return false;\n }\n _getSelectorSafeElement(element) {\n var _a;\n let current = element;\n while (current) {\n const tagName = (_a = current.tagName) == null ? void 0 : _a.toLowerCase();\n if (current.namespaceURI === \"http://www.w3.org/1999/xhtml\") {\n return current;\n }\n if (current.namespaceURI === \"http://www.w3.org/2000/svg\") {\n current = current.parentElement;\n continue;\n }\n if (current.hasAttribute(\"data-testid\") || current.hasAttribute(\"data-item-id\") || current.hasAttribute(\"data-column-id\") || current.hasAttribute(\"draggable\")) {\n return current;\n }\n current = current.parentElement;\n }\n return element;\n }\n _isSliderThumb(element) {\n var _a, _b;\n const classes = typeof element.className === \"string\" ? element.className : ((_a = element.className) == null ? void 0 : _a.baseVal) || \"\";\n if (classes.includes(\"MuiSlider-mark\") || classes.includes(\"MuiSlider-markLabel\") || classes.includes(\"slider-label\") || classes.includes(\"slider-mark\"))\n return false;\n if (classes.includes(\"MuiSlider-thumb\"))\n return true;\n if (element.querySelector('input[type=\"range\"]'))\n return true;\n if (((_b = element.tagName) == null ? void 0 : _b.toLowerCase()) === \"input\" && element.type === \"range\")\n return true;\n if (classes.includes(\"slider-thumb\") || classes.includes(\"rc-slider-handle\") || classes.includes(\"noUi-handle\") || element.hasAttribute(\"role\") && element.getAttribute(\"role\") === \"slider\")\n return true;\n return false;\n }\n _findSliderThumb(element) {\n if (this._isSliderThumb(element))\n return element;\n if (element.parentElement && this._isSliderThumb(element.parentElement))\n return element.parentElement;\n return null;\n }\n _findSliderRoot(element) {\n var _a, _b;\n let current = element;\n for (let i = 0; i < 5 && current; i++) {\n const classes = typeof current.className === \"string\" ? current.className : ((_a = current.className) == null ? void 0 : _a.baseVal) || \"\";\n const tagName = ((_b = current.tagName) == null ? void 0 : _b.toLowerCase()) || \"\";\n if (tagName === \"input\" && current.type === \"range\")\n return current;\n if (classes.includes(\"MuiSlider-root\"))\n return current;\n if (classes.includes(\"rc-slider\") || classes.includes(\"noUi-target\") || classes.includes(\"slider-container\") || current.hasAttribute(\"role\") && current.getAttribute(\"role\") === \"slider\")\n return current;\n current = current.parentElement;\n }\n return null;\n }\n _shouldIgnoreForSlider(element) {\n var _a, _b;\n const classes = typeof element.className === \"string\" ? element.className : ((_a = element.className) == null ? void 0 : _a.baseVal) || \"\";\n const tagName = ((_b = element.tagName) == null ? void 0 : _b.toLowerCase()) || \"\";\n if (classes.includes(\"MuiSlider-markLabel\") || classes.includes(\"MuiSlider-mark\") || classes.includes(\"MuiSlider-valueLabel\") || classes.includes(\"slider-label\"))\n return true;\n if ((tagName === \"span\" || tagName === \"div\") && !classes.includes(\"MuiSlider-thumb\") && !classes.includes(\"slider-thumb\"))\n return true;\n return false;\n }\n _selectDraggableAncestor(element) {\n var _a;\n const selectors = [\n \"[data-item-id]\",\n // Specific to the kanban board items\n '[draggable=\"true\"]',\n \"[data-draggable]\",\n '[role=\"listitem\"]',\n \".draggable\",\n '[data-testid*=\"drag\"]'\n ];\n let current = element;\n const tagName = (_a = current.tagName) == null ? void 0 : _a.toLowerCase();\n if (tagName === \"button\" || tagName === \"input\" || tagName === \"select\" || tagName === \"textarea\" || tagName === \"a\") {\n current = current.parentElement;\n }\n for (let i = 0; i < 5 && current; i++) {\n if (current.hasAttribute(\"data-item-id\")) {\n return current;\n }\n if (selectors.some((sel) => {\n var _a2;\n return (_a2 = current == null ? void 0 : current.matches) == null ? void 0 : _a2.call(current, sel);\n })) {\n return current;\n }\n current = current.parentElement;\n }\n return element;\n }\n _getSourceColumn(element) {\n const sourceColumnId = element.getAttribute(\"data-source-column\");\n if (sourceColumnId) {\n const column = this._recorder.document.querySelector(`[data-column-id=\"${sourceColumnId}\"]`);\n if (column) return column;\n }\n let current = element;\n for (let i = 0; i < 10 && current; i++) {\n if (current.hasAttribute(\"data-column-id\")) {\n return current;\n }\n current = current.parentElement;\n }\n return null;\n }\n _selectDroppableAncestor(element) {\n let current = element;\n if (current.hasAttribute(\"data-item-id\") || current.hasAttribute(\"draggable\")) {\n current = current.parentElement;\n }\n for (let i = 0; i < 8 && current; i++) {\n if (current.hasAttribute(\"data-column-id\")) {\n return current;\n }\n if (current.hasAttribute(\"data-droppable\") && current.hasAttribute(\"data-testid\")) {\n const testId = current.getAttribute(\"data-testid\");\n if (testId && testId.startsWith(\"column-\")) {\n return current;\n }\n }\n if (current.hasAttribute(\"data-drop-target-for-element\") && current.hasAttribute(\"data-testid\")) {\n const testId = current.getAttribute(\"data-testid\");\n if (testId && testId.startsWith(\"calendar-cell-\")) {\n return current;\n }\n }\n current = current.parentElement;\n }\n current = element;\n if (current.hasAttribute(\"data-item-id\") || current.hasAttribute(\"draggable\")) {\n current = current.parentElement;\n }\n const fallbackSelectors = [\n \"[data-droppable]\",\n \"[data-drop-target-for-element]\",\n '[role=\"list\"]',\n '[role=\"listbox\"]',\n '[role=\"grid\"]',\n \".droppable\",\n \".drop-zone\",\n \"[data-drop-zone]\",\n // Library-specific drop-zone classes (SKYR-3706)\n \".vue-grid-layout\",\n \".react-grid-layout\",\n \"[data-rbd-droppable-id]\",\n \"[data-sortable]\"\n ];\n for (let i = 0; i < 8 && current; i++) {\n if (fallbackSelectors.some((sel) => {\n var _a;\n return (_a = current == null ? void 0 : current.matches) == null ? void 0 : _a.call(current, sel);\n })) {\n return current;\n }\n current = current.parentElement;\n }\n current = element;\n if (current.hasAttribute(\"data-item-id\") || current.hasAttribute(\"draggable\")) {\n current = current.parentElement;\n }\n for (let i = 0; i < 8 && current; i++) {\n if (current.querySelectorAll(':scope > [draggable=\"true\"]').length >= 2) {\n return current;\n }\n current = current.parentElement;\n }\n return element;\n }\n _relativePoint(el, clientX, clientY) {\n const r = el.getBoundingClientRect();\n return {\n x: Math.max(0, Math.min(clientX - r.left, r.width)),\n y: Math.max(0, Math.min(clientY - r.top, r.height))\n };\n }\n // SKYR-3706: Returns `#<id>` if the element has a stable-looking developer-chosen\n // id, otherwise null. Used to bypass Playwright's default selector generator for\n // drag/drop, which otherwise prefers text/role for unidentified divs and produces\n // brittle selectors like `internal:text=\"1行テキスト\"` for palette items that have\n // a perfectly good id like `#item_input`.\n //\n // Strict heuristic: id must start with a letter, contain no whitespace, and every\n // part (split on `_` or `-`) must be purely alphabetic. This accepts `item_input`,\n // `divRight`, `submit-button` but rejects framework-generated ids like\n // `mat-select-1234`, `section1_466`, `radix-r1`, `:r1:`, `elem-a3f9b2e1c0`. False\n // negatives are cheap (fall back to default selector); false positives are\n // expensive (selector breaks across runs).\n _stableIdSelector(element) {\n const id = element.getAttribute(\"id\");\n if (!id || !/^[a-zA-Z]/.test(id) || /\\s/.test(id))\n return null;\n if (!id.split(/[-_]/).every((part) => part.length > 0 && /^[a-zA-Z]+$/.test(part)))\n return null;\n return { selector: `#${id}` };\n }\n // SKYR-3706: When the drop target is a recognized drop-zone container\n // (vue-grid-layout, react-grid-layout), use the class as the selector\n // directly. Without this, Playwright's generateSelector picks the container's\n // accumulated innerText (every form item's label concatenated), which produces\n // an extremely brittle `internal:text=\"WF名 ※ WF期限 1行テキスト …\"` selector\n // that grows after each drop and only matches the exact previous-drop sequence.\n // Order matters: callers should try `_stableIdSelector` first (more specific),\n // then this. Only the canonical drop-zone classes are recognized — generic\n // utility classes like `sortable` are too common and would over-match.\n _stableContainerClassSelector(element) {\n const dropZoneClasses = [\"vue-grid-layout\", \"react-grid-layout\"];\n for (const cls of dropZoneClasses) {\n if (element.classList.contains(cls))\n return { selector: `.${cls}` };\n }\n return null;\n }\n // SKYR-3706: Returns a human-readable label for the drag source. Used by Skyramp\n // codegen to emit `expect(target).toContainText(label)` between consecutive drops\n // into the same drop zone, providing a settle signal that doesn't require a\n // network response. Priority: aria-label > textContent > title. innerText\n // beats title because tooltips are often generic (\"Drag to add\", \"Click to\n // edit\") and identical across siblings, while innerText is the discriminating\n // label of the specific item (\"1行テキスト\", \"チェックボックス\"). Returns empty\n // string if no usable label found (codegen falls back to a fixed delay).\n _extractSourceLabel(element) {\n const ariaLabel = element.getAttribute(\"aria-label\");\n if (ariaLabel && ariaLabel.trim())\n return ariaLabel.trim().slice(0, 80);\n const text = element.innerText || element.textContent || \"\";\n const trimmed = text.trim().replace(/\\s+/g, \" \");\n if (trimmed)\n return trimmed.slice(0, 80);\n const title = element.getAttribute(\"title\");\n if (title && title.trim())\n return title.trim().slice(0, 80);\n return \"\";\n }\n _isCenter(point, element) {\n const rect = element.getBoundingClientRect();\n const centerX = rect.width / 2;\n const centerY = rect.height / 2;\n const threshold = 5;\n return Math.abs(point.x - centerX) < threshold && Math.abs(point.y - centerY) < threshold;\n }\n _capture() {\n var _a, _b, _c, _d;\n if (!this._dragState || !this._dragState.source || !this._dragState.target) {\n return;\n }\n if (this._dragState.captured) {\n return;\n }\n this._dragState.captured = true;\n if (this._dragState.isReactFlow) {\n this._captureReactFlowDrag();\n return;\n }\n if (this._dragState.isGoJS) {\n this._captureGoJSDrag();\n return;\n }\n if (this._dragState.isCanvas) {\n this._captureCanvasDrag();\n return;\n }\n if (this._dragState.isSlider) {\n this._captureSliderDrag();\n return;\n }\n const sourceColumn = this._getSourceColumn(this._dragState.source);\n const targetColumn = this._dragState.target.hasAttribute(\"data-column-id\") ? this._dragState.target : this._getSourceColumn(this._dragState.target);\n const isSameColumn = sourceColumn && targetColumn && sourceColumn.getAttribute(\"data-column-id\") === targetColumn.getAttribute(\"data-column-id\");\n try {\n const sourceTestId = this._dragState.source.getAttribute(\"data-testid\") || this._dragState.source.getAttribute(\"data-item-id\");\n const targetTestId = this._dragState.target.getAttribute(\"data-testid\") || this._dragState.target.getAttribute(\"data-column-id\");\n let sourceGenerated;\n let targetGenerated;\n if (sourceTestId && targetTestId) {\n sourceGenerated = { selector: `[data-testid=\"${sourceTestId}\"]` };\n targetGenerated = { selector: `[data-testid=\"${targetTestId}\"]` };\n } else {\n const safeSource = this._getSelectorSafeElement(this._dragState.source);\n const safeTarget = this._getSelectorSafeElement(this._dragState.target);\n sourceGenerated = (_b = (_a = this._stableIdSelector(safeSource)) != null ? _a : this._stableContainerClassSelector(safeSource)) != null ? _b : this._recorder.injectedScript.generateSelector(safeSource, {\n testIdAttributeName: this._recorder.state.testIdAttributeName || \"data-testid\"\n });\n targetGenerated = (_d = (_c = this._stableIdSelector(safeTarget)) != null ? _c : this._stableContainerClassSelector(safeTarget)) != null ? _d : this._recorder.injectedScript.generateSelector(safeTarget, {\n testIdAttributeName: this._recorder.state.testIdAttributeName || \"data-testid\"\n });\n }\n const sourcePos = this._dragState.sourcePoint ? this._relativePoint(this._dragState.source, this._dragState.sourcePoint.x, this._dragState.sourcePoint.y) : { x: this._dragState.source.getBoundingClientRect().width / 2, y: this._dragState.source.getBoundingClientRect().height / 2 };\n const targetPos = this._dragState.targetPoint ? this._relativePoint(this._dragState.target, this._dragState.targetPoint.x, this._dragState.targetPoint.y) : { x: this._dragState.target.getBoundingClientRect().width / 2, y: this._dragState.target.getBoundingClientRect().height / 2 };\n const duration = Date.now() - this._dragState.startTime;\n const action = {\n name: \"dragTo\",\n selector: sourceGenerated.selector,\n target: targetGenerated.selector,\n signals: [],\n timestamp: getTimestamp(this._recorder)\n };\n if (!this._isCenter(sourcePos, this._dragState.source))\n action.sourcePosition = { x: Math.round(sourcePos.x), y: Math.round(sourcePos.y) };\n if (!this._isCenter(targetPos, this._dragState.target))\n action.targetPosition = { x: Math.round(targetPos.x), y: Math.round(targetPos.y) };\n if (duration > 500)\n action.duration = duration;\n const sourceLabel = this._extractSourceLabel(this._dragState.source);\n if (sourceLabel)\n action.sourceLabel = sourceLabel;\n this._recorder.recordAction(action);\n this._deactivate();\n } catch (error) {\n console.error(\"[PW-RECORDER] Error generating selectors:\", error);\n try {\n const sourceId = this._dragState.source.getAttribute(\"data-testid\") || this._dragState.source.getAttribute(\"data-item-id\");\n const targetId = this._dragState.target.getAttribute(\"data-testid\") || this._dragState.target.getAttribute(\"data-column-id\");\n if (sourceId && targetId) {\n const action = {\n name: \"dragTo\",\n selector: `[data-testid=\"${sourceId}\"]`,\n target: `[data-testid=\"${targetId}\"]`,\n signals: [],\n timestamp: getTimestamp(this._recorder)\n };\n const sourceLabel = this._extractSourceLabel(this._dragState.source);\n if (sourceLabel)\n action.sourceLabel = sourceLabel;\n this._recorder.recordAction(action);\n }\n } catch (fallbackError) {\n console.error(\"[PW-RECORDER] Fallback also failed:\", fallbackError);\n }\n this._deactivate();\n }\n this._dragState = {\n source: null,\n target: null,\n sourcePoint: null,\n targetPoint: null,\n startTime: Date.now(),\n isCanvas: false,\n isGoJS: false,\n isReactFlow: false,\n isSlider: false,\n dropDetected: false,\n captured: false\n };\n }\n /**\n * Helper to round position coordinates to ensure integer pixel values\n */\n _roundPosition(pos) {\n return { x: Math.round(pos.x), y: Math.round(pos.y) };\n }\n /**\n * Records a mouse drag operation as 4 separate mouse actions.\n *\n * This helper generates the sequence: mouse.move -> mouse.down -> mouse.move (with steps) -> mouse.up\n * which creates a realistic drag interaction. Used by both React Flow and slider drag operations\n * to generate low-level mouse actions instead of high-level dragTo operations.\n *\n * @param sourcePos - Starting position of the drag (viewport coordinates)\n * @param targetPos - Ending position of the drag (viewport coordinates)\n * @param steps - Number of intermediate steps for smooth movement (default: 10)\n */\n _recordMouseDragActions(sourcePos, targetPos, steps = 10) {\n const mouseMoveStart = {\n name: \"mouse.move\",\n position: this._roundPosition(sourcePos),\n signals: [],\n timestamp: getTimestamp(this._recorder)\n };\n this._recorder.recordAction(mouseMoveStart);\n const mouseDown = {\n name: \"mouse.down\",\n signals: [],\n timestamp: getTimestamp(this._recorder)\n };\n this._recorder.recordAction(mouseDown);\n const mouseMoveEnd = {\n name: \"mouse.move\",\n position: this._roundPosition(targetPos),\n steps,\n signals: [],\n timestamp: getTimestamp(this._recorder)\n };\n this._recorder.recordAction(mouseMoveEnd);\n const mouseUp = {\n name: \"mouse.up\",\n signals: [],\n timestamp: getTimestamp(this._recorder)\n };\n this._recorder.recordAction(mouseUp);\n }\n _captureReactFlowDrag() {\n if (!this._dragState || !this._dragState.source || !this._dragState.target) {\n return;\n }\n try {\n const reactFlowContainer = this._dragState.source;\n const containerRect = reactFlowContainer.getBoundingClientRect();\n const sourceAbsolutePos = this._dragState.sourcePoint ? { x: this._dragState.sourcePoint.x, y: this._dragState.sourcePoint.y } : { x: containerRect.left + containerRect.width / 2, y: containerRect.top + containerRect.height / 2 };\n const targetAbsolutePos = this._dragState.targetPoint ? { x: this._dragState.targetPoint.x, y: this._dragState.targetPoint.y } : { x: containerRect.left + containerRect.width / 2, y: containerRect.top + containerRect.height / 2 };\n this._recordMouseDragActions(sourceAbsolutePos, targetAbsolutePos, 10);\n } catch (error) {\n console.error(\"[PW-RECORDER] Error capturing React Flow drag:\", error);\n }\n this._deactivate();\n this._dragState = {\n source: null,\n target: null,\n sourcePoint: null,\n targetPoint: null,\n startTime: Date.now(),\n isCanvas: false,\n isGoJS: false,\n isReactFlow: false,\n isSlider: false,\n dropDetected: false,\n captured: false\n };\n }\n _captureCanvasDrag() {\n if (!this._dragState || !this._dragState.source || !this._dragState.target) {\n return;\n }\n try {\n const canvasElement = this._dragState.source;\n const sourceGenerated = this._recorder.injectedScript.generateSelector(canvasElement, {\n testIdAttributeName: this._recorder.state.testIdAttributeName || \"data-testid\"\n });\n const sourcePos = this._dragState.sourcePoint ? this._relativePoint(canvasElement, this._dragState.sourcePoint.x, this._dragState.sourcePoint.y) : { x: canvasElement.getBoundingClientRect().width / 2, y: canvasElement.getBoundingClientRect().height / 2 };\n const targetPos = this._dragState.targetPoint ? this._relativePoint(canvasElement, this._dragState.targetPoint.x, this._dragState.targetPoint.y) : { x: canvasElement.getBoundingClientRect().width / 2, y: canvasElement.getBoundingClientRect().height / 2 };\n const duration = Date.now() - this._dragState.startTime;\n const action = {\n name: \"dragTo\",\n selector: sourceGenerated.selector,\n target: sourceGenerated.selector,\n sourcePosition: { x: Math.round(sourcePos.x), y: Math.round(sourcePos.y) },\n targetPosition: { x: Math.round(targetPos.x), y: Math.round(targetPos.y) },\n signals: [],\n timestamp: getTimestamp(this._recorder)\n };\n if (duration > 500)\n action.duration = duration;\n this._recorder.recordAction(action);\n } catch (error) {\n console.error(\"[PW-RECORDER] Error capturing canvas drag:\", error);\n }\n this._deactivate();\n this._dragState = {\n source: null,\n target: null,\n sourcePoint: null,\n targetPoint: null,\n startTime: Date.now(),\n isCanvas: false,\n isGoJS: false,\n isReactFlow: false,\n isSlider: false,\n dropDetected: false,\n captured: false\n };\n }\n _captureSliderDrag() {\n if (!this._dragState || !this._dragState.source || !this._dragState.sourcePoint || !this._dragState.targetPoint) {\n return;\n }\n try {\n const sourcePos = {\n x: Math.round(this._dragState.sourcePoint.x),\n y: Math.round(this._dragState.sourcePoint.y)\n };\n const targetPos = {\n x: Math.round(this._dragState.targetPoint.x),\n y: Math.round(this._dragState.targetPoint.y)\n };\n const dx = targetPos.x - sourcePos.x;\n const dy = targetPos.y - sourcePos.y;\n const distance = Math.sqrt(dx * dx + dy * dy);\n const steps = Math.max(1, Math.floor(distance / _DragDropTool.PIXELS_PER_STEP));\n const sliderElement = this._dragState.source;\n let sliderInfo = \"\";\n let direction = \"\";\n if (Math.abs(dx) > Math.abs(dy)) {\n direction = dx > 0 ? \"right\" : \"left\";\n } else {\n direction = dy > 0 ? \"down\" : \"up\";\n }\n const getSliderInfo = (element, visited = /* @__PURE__ */ new Set()) => {\n if (visited.has(element)) {\n return null;\n }\n visited.add(element);\n const ariaValue = element.getAttribute(\"aria-valuenow\");\n const ariaMin = element.getAttribute(\"aria-valuemin\");\n const ariaMax = element.getAttribute(\"aria-valuemax\");\n if (ariaValue) {\n return {\n value: ariaValue,\n min: ariaMin || void 0,\n max: ariaMax || void 0\n };\n }\n if (element instanceof HTMLInputElement && element.type === \"range\") {\n return {\n value: element.value,\n min: element.min || void 0,\n max: element.max || void 0\n };\n }\n const children = Array.from(element.children);\n for (const child of children) {\n if (!visited.has(child)) {\n if (child instanceof HTMLInputElement && child.type === \"range\" || child.hasAttribute(\"aria-valuenow\")) {\n const result = getSliderInfo(child, visited);\n if (result) return result;\n }\n }\n }\n if (element.parentElement) {\n const siblings = Array.from(element.parentElement.children);\n for (const sibling of siblings) {\n if (sibling !== element && !visited.has(sibling)) {\n if (sibling instanceof HTMLInputElement && sibling.type === \"range\" || sibling.hasAttribute(\"aria-valuenow\")) {\n const result = getSliderInfo(sibling, visited);\n if (result) return result;\n }\n }\n }\n if (!visited.has(element.parentElement)) {\n const parentAriaValue = element.parentElement.getAttribute(\"aria-valuenow\");\n if (parentAriaValue) {\n return {\n value: parentAriaValue,\n min: element.parentElement.getAttribute(\"aria-valuemin\") || void 0,\n max: element.parentElement.getAttribute(\"aria-valuemax\") || void 0\n };\n }\n }\n }\n return null;\n };\n const sliderData = getSliderInfo(sliderElement);\n if (sliderData && sliderData.value) {\n const rangeInfo = sliderData.min && sliderData.max ? ` (range: ${sliderData.min} to ${sliderData.max})` : \"\";\n sliderInfo = ` to value ${sliderData.value}${rangeInfo}`;\n }\n const commentAction = {\n name: \"comment\",\n text: `Moving slider ${direction}${sliderInfo}`,\n signals: [],\n timestamp: getTimestamp(this._recorder)\n };\n this._recorder.recordAction(commentAction);\n this._recordMouseDragActions(sourcePos, targetPos, steps);\n } catch (error) {\n console.error(\"[PW-RECORDER] Error capturing slider drag:\", error);\n }\n this._deactivate();\n this._dragState = {\n source: null,\n target: null,\n sourcePoint: null,\n targetPoint: null,\n startTime: Date.now(),\n isCanvas: false,\n isGoJS: false,\n isReactFlow: false,\n isSlider: false,\n dropDetected: false,\n captured: false\n };\n }\n};\n// Configuration constants for smooth mouse movements\n_DragDropTool.PIXELS_PER_STEP = 5;\n// ~5px per step for smooth slider dragging\n_DragDropTool.WHEEL_DEBOUNCE_MS = 500;\n// Wait 500ms after last wheel event before recording\n_DragDropTool.WHEEL_MAX_ACCUMULATION_MS = 1e3;\n// Max time to accumulate before forcing a record\n_DragDropTool.WHEEL_TOOL_DISABLE_MS = 1e3;\n// Wait 1000ms after last wheel event before disabling DD tool\n_DragDropTool.WHEEL_SCROLL_TIMEOUT_MS = 3e3;\n// Wait after scroll block comment to let the page settle\n// Mac Magic Mouse / trackpad touch surfaces generate stray wheel events\n// whenever a finger drags across them — even when the user is just moving\n// the cursor, not scrolling. Those flutter sequences typically accumulate\n// to small bidirectional deltas (both |deltaX| and |deltaY| under ~30px,\n// often with mixed signs). Recording them produces synthesized\n// page.mouse.wheel() calls on replay that scroll real content under the\n// pointer and can move the next click target out of view.\n//\n// Filter rule at flush time: if neither accumulated axis crossed this\n// threshold, treat the sequence as Magic Mouse noise and don't emit the\n// mouse.wheel action. The mouse.move/comment/waitForTimeout actions that\n// were emitted at the start of the sequence stay in the trace; they are\n// harmless on replay (cursor move + 3s wait) and removing them would\n// require buffering — out of scope for this filter.\n_DragDropTool.WHEEL_NOISE_AXIS_THRESHOLD = 30;\nvar DragDropTool = _DragDropTool;\n\n// packages/injected/src/recorder/skyramp/gojsLinkTool.ts\nfunction getTimestamp2(recorder) {\n return recorder.injectedScript.utils.builtins.Date.now().toString();\n}\nvar GoJSLinkTool = class {\n constructor(recorder) {\n this._diagramEntries = [];\n this._keydownRemover = null;\n this._recorder = recorder;\n }\n cursor() {\n return \"crosshair\";\n }\n install() {\n console.log(\"[GoJSLinkTool] install() \\u2014 document:\", this._recorder.document.URL);\n this._recorder.document.__skyrampGojsLinkToolActive = true;\n this._diagramEntries = [];\n const canvases = Array.from(this._recorder.document.querySelectorAll(\"canvas\"));\n console.log(\"[GoJSLinkTool] found canvases:\", canvases.length);\n for (const canvas of canvases) {\n const entry = this._hookDiagram(canvas);\n if (entry) {\n this._diagramEntries.push(entry);\n console.log(\"[GoJSLinkTool] hooked diagram, panelSelector:\", entry.panelSelector);\n }\n }\n console.log(\"[GoJSLinkTool] hooked\", this._diagramEntries.length, \"diagram(s)\");\n const onKeyDown = (e) => {\n if (e.key === \"Escape\") {\n this._recorder.setMode(\"recording\");\n e.preventDefault();\n e.stopPropagation();\n }\n };\n this._recorder.document.addEventListener(\"keydown\", onKeyDown, true);\n this._keydownRemover = () => this._recorder.document.removeEventListener(\"keydown\", onKeyDown, true);\n }\n uninstall() {\n var _a;\n console.log(\"[GoJSLinkTool] uninstall() \\u2014 document:\", this._recorder.document.URL);\n delete this._recorder.document.__skyrampGojsLinkToolActive;\n for (const entry of this._diagramEntries) {\n try {\n entry.diagram.allowMove = entry.prevAllowMove;\n const lt = (_a = entry.diagram.toolManager) == null ? void 0 : _a.linkingTool;\n if (lt) lt.isEnabled = entry.prevLinkingEnabled;\n entry.diagram.removeDiagramListener(\"LinkDrawn\", entry.linkDrawnHandler);\n entry.diagram.removeDiagramListener(\"ExternalObjectsDropped\", entry.externalDropHandler);\n console.log(\"[GoJSLinkTool] restored diagram, panelSelector:\", entry.panelSelector);\n } catch (_e) {\n console.log(\"[GoJSLinkTool] error restoring diagram:\", _e);\n }\n }\n this._diagramEntries = [];\n if (this._keydownRemover) {\n this._keydownRemover();\n this._keydownRemover = null;\n }\n }\n _hookDiagram(canvas) {\n var _a, _b, _c, _d, _e, _f;\n const win = (_a = canvas.ownerDocument) == null ? void 0 : _a.defaultView;\n if (!((_c = (_b = win == null ? void 0 : win.go) == null ? void 0 : _b.Diagram) == null ? void 0 : _c.fromDiv))\n return null;\n let el = canvas.parentElement;\n while (el && el !== canvas.ownerDocument.body) {\n const diagram = win.go.Diagram.fromDiv(el);\n if (diagram) {\n const panelSelector = this._buildSelector(el);\n const prevAllowMove = diagram.allowMove;\n diagram.allowMove = false;\n const lt = (_d = diagram.toolManager) == null ? void 0 : _d.linkingTool;\n const prevLinkingEnabled = (_e = lt == null ? void 0 : lt.isEnabled) != null ? _e : true;\n if (lt) {\n lt.isEnabled = true;\n if (((_f = lt.portGravity) != null ? _f : 0) < 10)\n lt.portGravity = 10;\n }\n const linkDrawnHandler = (e) => {\n var _a2, _b2, _c2, _d2;\n const link = e.subject;\n if (!(link == null ? void 0 : link.data)) return;\n const fromKey = String((_a2 = link.data.from) != null ? _a2 : \"\");\n const toKey = String((_b2 = link.data.to) != null ? _b2 : \"\");\n if (!fromKey || !toKey) return;\n console.log(\"[GoJSLinkTool] LinkDrawn from:\", fromKey, \"to:\", toKey);\n this._emitDiagramLinkAdd(\n fromKey,\n toKey,\n String((_c2 = link.data.fromPort) != null ? _c2 : \"\"),\n String((_d2 = link.data.toPort) != null ? _d2 : \"\"),\n panelSelector\n );\n };\n diagram.addDiagramListener(\"LinkDrawn\", linkDrawnHandler);\n const externalDropHandler = (e) => {\n e.subject.each((part) => {\n var _a2, _b2;\n if (!(part == null ? void 0 : part.data)) return;\n if (part.data.from !== void 0) return;\n const category = String((_a2 = part.data.category) != null ? _a2 : \"\");\n const key = String((_b2 = part.data.key) != null ? _b2 : \"\");\n const loc = part.location;\n console.log(\"[GoJSLinkTool] ExternalObjectsDropped category:\", category, \"key:\", key, \"loc:\", loc == null ? void 0 : loc.x, loc == null ? void 0 : loc.y);\n setTimeout(() => {\n var _a3, _b3, _c2, _d2, _e2, _f2;\n this._emitDiagramNodeAdd(diagram, panelSelector, category, key, (_c2 = (_b3 = (_a3 = part.location) == null ? void 0 : _a3.x) != null ? _b3 : loc == null ? void 0 : loc.x) != null ? _c2 : 0, (_f2 = (_e2 = (_d2 = part.location) == null ? void 0 : _d2.y) != null ? _e2 : loc == null ? void 0 : loc.y) != null ? _f2 : 0);\n }, 0);\n });\n };\n diagram.addDiagramListener(\"ExternalObjectsDropped\", externalDropHandler);\n return { diagram, panelSelector, prevAllowMove, prevLinkingEnabled, linkDrawnHandler, externalDropHandler };\n }\n el = el.parentElement;\n }\n return null;\n }\n _emitDiagramLinkAdd(fromKey, toKey, fromPort, toPort, panelSelector) {\n var _a;\n const action = {\n name: \"diagramLinkAdd\",\n diagramType: \"gojs\",\n panelSelector,\n fromKey,\n toKey,\n fromPort,\n toPort,\n signals: [],\n timestamp: getTimestamp2(this._recorder)\n };\n this._recorder.recordAction(action);\n (_a = this._recorder.overlay) == null ? void 0 : _a.flashToolSucceeded(\"recordingGoJSLink\");\n }\n /**\n * Emit a diagramNodeAdd action for a palette → canvas drop.\n * Computes anchorKey, anchorOffsetX/Y, and anchorDocX/Y by finding the nearest\n * existing node in the diagram to serve as a stable reference point. Anchor\n * candidates include all nodes currently in the diagram — both pre-existing nodes\n * and any nodes added earlier in this recording session.\n */\n _emitDiagramNodeAdd(diagram, targetPanelSelector, category, key, docX, docY) {\n var _a, _b;\n const paletteEntry = this._diagramEntries.find((entry) => {\n var _a2, _b2, _c;\n try {\n const win = (_b2 = (_a2 = entry.diagram.div) == null ? void 0 : _a2.ownerDocument) == null ? void 0 : _b2.defaultView;\n return ((_c = win == null ? void 0 : win.go) == null ? void 0 : _c.Palette) && entry.diagram instanceof win.go.Palette;\n } catch (_) {\n return false;\n }\n });\n const sourcePanelSelector = (_a = paletteEntry == null ? void 0 : paletteEntry.panelSelector) != null ? _a : \"\";\n let anchorKey;\n let anchorOffsetX;\n let anchorOffsetY;\n let anchorDocX;\n let anchorDocY;\n let minDist = Infinity;\n diagram.nodes.each((node) => {\n var _a2;\n if (!(node == null ? void 0 : node.data)) return;\n const nKey = String((_a2 = node.data.key) != null ? _a2 : \"\");\n if (!nKey || nKey === key) return;\n const dx = node.location.x - docX;\n const dy = node.location.y - docY;\n const dist = Math.sqrt(dx * dx + dy * dy);\n if (dist < minDist) {\n minDist = dist;\n anchorKey = nKey;\n anchorOffsetX = Math.round(docX - node.location.x);\n anchorOffsetY = Math.round(docY - node.location.y);\n anchorDocX = Math.round(node.location.x);\n anchorDocY = Math.round(node.location.y);\n }\n });\n const action = {\n name: \"diagramNodeAdd\",\n diagramType: \"gojs\",\n sourcePanelSelector,\n targetPanelSelector,\n sourceIsPalette: true,\n targetIsPalette: false,\n sourceCategory: category,\n sourceKey: key,\n targetDocX: Math.round(docX),\n targetDocY: Math.round(docY),\n anchorKey,\n anchorOffsetX,\n anchorOffsetY,\n anchorDocX,\n anchorDocY,\n signals: [],\n timestamp: getTimestamp2(this._recorder)\n };\n this._recorder.recordAction(action);\n (_b = this._recorder.overlay) == null ? void 0 : _b.flashToolSucceeded(\"recordingGoJSLink\");\n }\n _buildSelector(el) {\n if (el.id)\n return `#${el.id}`;\n if (el.getAttribute(\"data-testid\"))\n return `[data-testid=\"${el.getAttribute(\"data-testid\")}\"]`;\n const parent = el.parentElement;\n if (parent) {\n const idx = Array.from(parent.children).indexOf(el) + 1;\n return `${el.tagName.toLowerCase()}:nth-child(${idx})`;\n }\n return el.tagName.toLowerCase();\n }\n};\n\n// packages/injected/src/recorder/skyramp/fileUploadTool.ts\nfunction consumeEvent(e) {\n e.preventDefault();\n e.stopPropagation();\n e.stopImmediatePropagation();\n}\nfunction getTimestamp3(recorder) {\n return recorder.injectedScript.utils.builtins.Date.now().toString();\n}\nvar FileUploadTool = class {\n constructor(recorder) {\n this._triggerElement = null;\n // Cached at arm time so the selector is generated against the LIVE, still-\n // attached trigger element (typically the menuitem the user clicked). By\n // resolved-time the menuitem is often detached — generateSelector then\n // throws inside cssFallback/parseSelectorString and the entire resolved\n // handler aborts before recording the action. Caching here makes the\n // recorded action robust to that detachment.\n this._triggerSelector = null;\n this._input = null;\n this._pendingFiles = [];\n this._helperOverlay = null;\n this._recorder = recorder;\n }\n cursor() {\n return \"pointer\";\n }\n install() {\n const win = this._recorder.injectedScript.window;\n win.__pwRecorderFileChooserArmed = (event) => {\n if (event.input && event.triggerElement === event.input) {\n return;\n }\n this._triggerElement = event.triggerElement;\n this._input = event.input;\n this._triggerSelector = event.triggerSelector || null;\n if (!this._triggerSelector && event.triggerElement) {\n try {\n this._triggerSelector = this._recorder.injectedScript.generateSelector(event.triggerElement, {\n testIdAttributeName: this._recorder.state.testIdAttributeName,\n multiple: false\n }).selector;\n } catch (e) {\n console.warn(\"[FileUploadTool] arm-time selector fallback failed:\", e);\n }\n }\n this._showHelperOverlay(\"File chooser opening... Please select a file\");\n };\n win.__pwRecorderFileChooserResolved = (event) => {\n var _a;\n if (event.input && !this._triggerElement) {\n return;\n }\n this._pendingFiles = event.files;\n this._hideHelperOverlay();\n if (this._triggerElement && event.files.length > 0) {\n const filePaths = event.files.map((f) => f.name);\n let selector = this._triggerSelector;\n if (!selector) {\n try {\n selector = this._recorder.injectedScript.generateSelector(this._triggerElement, {\n testIdAttributeName: this._recorder.state.testIdAttributeName,\n multiple: false\n }).selector;\n } catch (e) {\n console.warn(\"[FileUploadTool] resolved-time selector generation failed:\", e);\n }\n }\n if (selector) {\n const action = {\n name: \"fileChooser\",\n selector,\n files: filePaths,\n signals: [],\n timestamp: getTimestamp3(this._recorder)\n };\n this._recorder.recordAction(action);\n this._recorder.setMode(\"recording\");\n (_a = this._recorder.overlay) == null ? void 0 : _a.flashToolSucceeded(\"fileUpload\");\n } else {\n console.warn(\"[FileUploadTool] no selector available for trigger element; action not recorded\");\n }\n } else {\n console.warn(\"[FileUploadTool] Missing trigger element or no files selected\");\n }\n };\n this._showHelperOverlay(\"Click a button to upload a file\");\n }\n uninstall() {\n const win = this._recorder.injectedScript.window;\n win.__pwRecorderFileChooserArmed = void 0;\n win.__pwRecorderFileChooserResolved = void 0;\n this._hideHelperOverlay();\n this._triggerElement = null;\n this._triggerSelector = null;\n this._input = null;\n this._pendingFiles = [];\n }\n onClick(event) {\n const recordTool = this._getRecordActionTool();\n if (!recordTool || !recordTool.onClick) {\n return;\n }\n recordTool.onClick(event);\n }\n onInput(event) {\n var _a;\n const target = this._recorder.deepEventTarget(event);\n if (target.nodeName === \"INPUT\" && target.type.toLowerCase() === \"file\") {\n if (this._triggerElement) {\n return;\n }\n const inputElement = target;\n const generated = this._recorder.injectedScript.generateSelector(inputElement, {\n testIdAttributeName: this._recorder.state.testIdAttributeName,\n multiple: false\n });\n this._recorder.recordAction({\n name: \"setInputFiles\",\n selector: generated.selector,\n signals: [],\n files: [...inputElement.files || []].map((file) => file.name),\n timestamp: getTimestamp3(this._recorder)\n });\n this._recorder.setMode(\"recording\");\n (_a = this._recorder.overlay) == null ? void 0 : _a.flashToolSucceeded(\"fileUpload\");\n } else {\n const recordTool = this._getRecordActionTool();\n if (recordTool && recordTool.onInput) {\n recordTool.onInput(event);\n }\n }\n }\n onKeyDown(event) {\n if (event.key === \"Escape\") {\n consumeEvent(event);\n this._recorder.setMode(\"recording\");\n return;\n }\n const recordTool = this._getRecordActionTool();\n if (recordTool && recordTool.onKeyDown) {\n recordTool.onKeyDown(event);\n }\n }\n onKeyUp(event) {\n const recordTool = this._getRecordActionTool();\n if (recordTool && recordTool.onKeyUp) {\n recordTool.onKeyUp(event);\n }\n }\n onPointerDown(event) {\n const recordTool = this._getRecordActionTool();\n if (recordTool && recordTool.onPointerDown) {\n recordTool.onPointerDown(event);\n }\n }\n onPointerUp(event) {\n const recordTool = this._getRecordActionTool();\n if (recordTool && recordTool.onPointerUp) {\n recordTool.onPointerUp(event);\n }\n }\n onPointerMove(event) {\n const recordTool = this._getRecordActionTool();\n if (recordTool && recordTool.onPointerMove) {\n recordTool.onPointerMove(event);\n }\n }\n onMouseMove(event) {\n const recordTool = this._getRecordActionTool();\n if (recordTool && recordTool.onMouseMove) {\n recordTool.onMouseMove(event);\n }\n }\n onMouseDown(event) {\n const recordTool = this._getRecordActionTool();\n if (recordTool && recordTool.onMouseDown) {\n recordTool.onMouseDown(event);\n }\n }\n onMouseUp(event) {\n const recordTool = this._getRecordActionTool();\n if (recordTool && recordTool.onMouseUp) {\n recordTool.onMouseUp(event);\n }\n }\n onMouseLeave(event) {\n const recordTool = this._getRecordActionTool();\n if (recordTool && recordTool.onMouseLeave) {\n recordTool.onMouseLeave(event);\n }\n }\n onFocus(event) {\n const recordTool = this._getRecordActionTool();\n if (recordTool && recordTool.onFocus) {\n recordTool.onFocus(event);\n }\n }\n _getRecordActionTool() {\n var _a;\n return ((_a = this._recorder._tools) == null ? void 0 : _a[\"recording\"]) || null;\n }\n _showHelperOverlay(message) {\n this._hideHelperOverlay();\n const overlay = this._recorder.document.createElement(\"div\");\n overlay.style.cssText = `\n position: fixed;\n top: 20px;\n left: 50%;\n transform: translateX(-50%);\n background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);\n color: white;\n padding: 12px 24px;\n border-radius: 8px;\n box-shadow: 0 4px 12px rgba(0, 0, 0, 0.3);\n z-index: 2147483645;\n font-family: system-ui, -apple-system, sans-serif;\n font-size: 14px;\n font-weight: 500;\n pointer-events: none;\n animation: pw-slide-down 0.3s ease-out;\n `;\n overlay.textContent = `\\u{1F4CE} ${message}`;\n const style = this._recorder.document.createElement(\"style\");\n style.textContent = `\n @keyframes pw-slide-down {\n from {\n opacity: 0;\n transform: translateX(-50%) translateY(-20px);\n }\n to {\n opacity: 1;\n transform: translateX(-50%) translateY(0);\n }\n }\n `;\n this._recorder.document.head.appendChild(style);\n this._recorder.document.body.appendChild(overlay);\n this._helperOverlay = overlay;\n }\n _hideHelperOverlay() {\n if (this._helperOverlay) {\n this._helperOverlay.remove();\n this._helperOverlay = null;\n }\n }\n};\n\n// packages/injected/src/recorder/skyramp/fileUploadHooks.ts\nfunction addEventListener2(target, eventName, listener, useCapture) {\n target.addEventListener(eventName, listener, useCapture);\n const remove = () => {\n target.removeEventListener(eventName, listener, useCapture);\n };\n return remove;\n}\nfunction installFileUploadHooks(recorder, listeners) {\n const win = recorder.injectedScript.window;\n const doc = recorder.document;\n let lastClickedElement = null;\n let lastClickedSelector = null;\n let lastClickTimestamp = 0;\n const instrumentedInputs = /* @__PURE__ */ new WeakSet();\n listeners.push(\n addEventListener2(doc, \"click\", (e) => {\n var _a;\n const event = e;\n const target = recorder.deepEventTarget(event);\n if (!event.isTrusted)\n return;\n if (target.nodeName === \"INPUT\" && target.type.toLowerCase() === \"file\")\n return;\n lastClickedElement = target;\n lastClickTimestamp = Date.now();\n try {\n lastClickedSelector = recorder.injectedScript.generateSelector(target, {\n testIdAttributeName: (_a = recorder.state) == null ? void 0 : _a.testIdAttributeName,\n multiple: false\n }).selector;\n } catch (err) {\n console.warn(\"[PW-FileUpload] click-capture selector generation failed:\", err);\n lastClickedSelector = null;\n }\n }, true)\n );\n const instrumentFileInput = (input) => {\n if (instrumentedInputs.has(input)) {\n return;\n }\n if (input.type !== \"file\") {\n return;\n }\n instrumentedInputs.add(input);\n const originalClick = input.click;\n input.click = function() {\n if (win.__pwRecorderFileChooserArmed) {\n win.__pwRecorderFileChooserArmed({\n triggerElement: lastClickedElement,\n triggerSelector: lastClickedSelector,\n input: this,\n timestamp: lastClickTimestamp\n });\n }\n return originalClick.apply(this, arguments);\n };\n addEventListener2(input, \"change\", () => {\n const files = Array.from(input.files || []).map((f) => ({\n name: f.name,\n size: f.size,\n type: f.type,\n lastModified: f.lastModified\n }));\n if (win.__pwRecorderFileChooserResolved && files.length > 0) {\n win.__pwRecorderFileChooserResolved({\n files,\n input\n });\n }\n }, true);\n };\n const originalInputClick = HTMLInputElement.prototype.click;\n HTMLInputElement.prototype.click = function() {\n if (this.type === \"file\") {\n instrumentFileInput(this);\n if (win.__pwRecorderFileChooserArmed) {\n win.__pwRecorderFileChooserArmed({\n triggerElement: lastClickedElement,\n triggerSelector: lastClickedSelector,\n input: this,\n timestamp: lastClickTimestamp\n });\n }\n }\n return originalInputClick.apply(this, arguments);\n };\n const originalCreateElement = Document.prototype.createElement;\n Document.prototype.createElement = function(tagName, options) {\n const element = originalCreateElement.call(this, tagName, options);\n if (element instanceof HTMLInputElement && element.type === \"file\") {\n instrumentFileInput(element);\n }\n return element;\n };\n const typeDescriptor = Object.getOwnPropertyDescriptor(HTMLInputElement.prototype, \"type\");\n if (typeDescriptor && typeDescriptor.set) {\n const originalTypeSetter = typeDescriptor.set;\n Object.defineProperty(HTMLInputElement.prototype, \"type\", {\n ...typeDescriptor,\n set(value) {\n const result = originalTypeSetter.call(this, value);\n if (String(value).toLowerCase() === \"file\") {\n instrumentFileInput(this);\n }\n return result;\n }\n });\n }\n if (\"showOpenFilePicker\" in win) {\n const originalShowOpenFilePicker = win.showOpenFilePicker;\n win.showOpenFilePicker = async function(...args) {\n if (win.__pwRecorderFileChooserArmed) {\n win.__pwRecorderFileChooserArmed({\n triggerElement: lastClickedElement,\n triggerSelector: lastClickedSelector,\n input: null,\n timestamp: lastClickTimestamp\n });\n }\n const handles = await originalShowOpenFilePicker.apply(this, args);\n const files = [];\n try {\n for (const handle of handles) {\n const file = await handle.getFile();\n files.push({\n name: file.name,\n size: file.size,\n type: file.type,\n lastModified: file.lastModified\n });\n }\n } catch (e) {\n console.warn(\"[PW-FileUpload] Failed to extract file metadata from handles:\", e);\n for (const handle of handles) {\n files.push({ name: handle.name || \"unknown\" });\n }\n }\n if (win.__pwRecorderFileChooserResolved && files.length > 0) {\n win.__pwRecorderFileChooserResolved({\n files,\n input: null\n });\n }\n return handles;\n };\n }\n const existingInputs = doc.querySelectorAll('input[type=\"file\"]');\n existingInputs.forEach((input) => {\n instrumentFileInput(input);\n });\n const observer = new MutationObserver((mutations) => {\n for (const mutation of mutations) {\n for (const node of mutation.addedNodes) {\n if (node instanceof HTMLInputElement && node.type === \"file\") {\n instrumentFileInput(node);\n }\n if (node instanceof Element) {\n const inputs = node.querySelectorAll('input[type=\"file\"]');\n inputs.forEach((input) => {\n instrumentFileInput(input);\n });\n }\n }\n }\n });\n observer.observe(doc.documentElement, { childList: true, subtree: true });\n listeners.push(() => {\n observer.disconnect();\n });\n}\n\n// packages/injected/src/recorder/skyramp/sketchTool.ts\nfunction getTimestamp4(recorder) {\n return recorder.injectedScript.utils.builtins.Date.now().toString();\n}\nvar SketchTool = class {\n // Minimum pixels between points\n constructor(recorder) {\n this._isDeleting = false;\n this._viewportPath = [];\n // Viewport coordinates (clientX, clientY)\n this._lastPointTime = 0;\n this.POINT_THROTTLE_MS = 33;\n // ~30fps\n this.MIN_DISTANCE = 5;\n this._recorder = recorder;\n }\n cursor() {\n return \"pointer\";\n }\n install() {\n this._recorder.injectedScript.document.body.classList.add(\"pw-sketch-tool-cursor\");\n this._createOverlayCanvas();\n }\n uninstall() {\n this._recorder.injectedScript.document.body.classList.remove(\"pw-sketch-tool-cursor\");\n this._removeOverlayCanvas();\n this._isDeleting = false;\n this._viewportPath = [];\n }\n onPointerDown(event) {\n if (event.button !== 0)\n return;\n const target = this._recorder.deepEventTarget(event);\n this._isDeleting = true;\n this._viewportPath = [];\n this._addPoint(event);\n this._clearOverlayPath();\n this._dispatchRealMouseEvent(\"mousedown\", event, target);\n return true;\n }\n onPointerMove(event) {\n if (!this._isDeleting)\n return;\n const target = this._recorder.deepEventTarget(event);\n this._dispatchRealMouseEvent(\"mousemove\", event, target);\n const now = this._recorder.injectedScript.utils.builtins.Date.now();\n if (now - this._lastPointTime < this.POINT_THROTTLE_MS)\n return;\n if (this._viewportPath.length > 0) {\n const lastPoint = this._viewportPath[this._viewportPath.length - 1];\n const distance = Math.hypot(\n event.clientX - lastPoint.x,\n event.clientY - lastPoint.y\n );\n if (distance >= this.MIN_DISTANCE) {\n this._addPoint(event);\n this._lastPointTime = now;\n this._updateOverlayPath();\n }\n }\n }\n onPointerUp(event) {\n var _a;\n if (!this._isDeleting)\n return;\n const target = this._recorder.deepEventTarget(event);\n this._dispatchRealMouseEvent(\"mouseup\", event, target);\n this._isDeleting = false;\n if (this._viewportPath.length > 1) {\n this._addPoint(event);\n this._recordSketchToolAsMouseActions();\n (_a = this._recorder.overlay) == null ? void 0 : _a.flashToolSucceeded(\"recordingSketchTool\");\n }\n this._clearOverlayPath();\n this._viewportPath = [];\n }\n onMouseDown(event) {\n return;\n }\n onMouseUp(event) {\n return;\n }\n onClick(event) {\n return;\n }\n _addPoint(event) {\n this._viewportPath.push({\n x: Math.round(event.clientX),\n y: Math.round(event.clientY)\n });\n }\n _recordSketchToolAsMouseActions() {\n const optimizedPath = this._optimizePath(this._viewportPath);\n if (optimizedPath.length === 0)\n return;\n const commentAction = {\n name: \"comment\",\n text: `Sketch tool with ${optimizedPath.length} path points`,\n signals: [],\n timestamp: getTimestamp4(this._recorder)\n };\n this._recorder.recordAction(commentAction);\n const firstPoint = optimizedPath[0];\n const mouseMoveToStart = {\n name: \"mouse.move\",\n position: firstPoint,\n signals: [],\n timestamp: getTimestamp4(this._recorder)\n };\n this._recorder.recordAction(mouseMoveToStart);\n const mouseDown = {\n name: \"mouse.down\",\n signals: [],\n timestamp: getTimestamp4(this._recorder)\n };\n this._recorder.recordAction(mouseDown);\n for (let i = 1; i < optimizedPath.length; i++) {\n const point = optimizedPath[i];\n const prevPoint = optimizedPath[i - 1];\n const dx = point.x - prevPoint.x;\n const dy = point.y - prevPoint.y;\n const distance = Math.sqrt(dx * dx + dy * dy);\n const steps = Math.max(1, Math.floor(distance / 5));\n const mouseMoveAction = {\n name: \"mouse.move\",\n position: point,\n steps,\n signals: [],\n timestamp: getTimestamp4(this._recorder)\n };\n this._recorder.recordAction(mouseMoveAction);\n }\n const mouseUp = {\n name: \"mouse.up\",\n signals: [],\n timestamp: getTimestamp4(this._recorder)\n };\n this._recorder.recordAction(mouseUp);\n }\n _optimizePath(path) {\n if (path.length < 5)\n return path;\n const optimized = this._douglasPeucker(path, 2);\n if (optimized.length < 5) {\n return this._douglasPeuckerWithMinPoints(path, 5);\n }\n return optimized;\n }\n _douglasPeucker(points, epsilon) {\n if (points.length <= 2)\n return points;\n let maxDist = 0;\n let maxIndex = 0;\n for (let i = 1; i < points.length - 1; i++) {\n const dist = this._perpendicularDistance(\n points[i],\n points[0],\n points[points.length - 1]\n );\n if (dist > maxDist) {\n maxDist = dist;\n maxIndex = i;\n }\n }\n if (maxDist > epsilon) {\n const left = this._douglasPeucker(\n points.slice(0, maxIndex + 1),\n epsilon\n );\n const right = this._douglasPeucker(\n points.slice(maxIndex),\n epsilon\n );\n return [...left.slice(0, -1), ...right];\n } else {\n return [points[0], points[points.length - 1]];\n }\n }\n _douglasPeuckerWithMinPoints(points, minPoints) {\n if (points.length <= minPoints)\n return points;\n let epsilon = 10;\n let result = this._douglasPeucker(points, epsilon);\n let high = 10;\n let low = 0;\n while (high - low > 0.1 && result.length !== minPoints) {\n epsilon = (high + low) / 2;\n result = this._douglasPeucker(points, epsilon);\n if (result.length < minPoints) {\n high = epsilon;\n } else if (result.length > minPoints) {\n low = epsilon;\n }\n }\n if (result.length < minPoints) {\n result = this._sampleEvenly(points, minPoints);\n }\n return result;\n }\n _sampleEvenly(points, count) {\n if (points.length <= count)\n return points;\n const result = [points[0]];\n const step = (points.length - 1) / (count - 1);\n for (let i = 1; i < count - 1; i++) {\n const index = Math.round(i * step);\n result.push(points[index]);\n }\n result.push(points[points.length - 1]);\n return result;\n }\n _perpendicularDistance(point, lineStart, lineEnd) {\n const dx = lineEnd.x - lineStart.x;\n const dy = lineEnd.y - lineStart.y;\n if (dx === 0 && dy === 0) {\n return Math.hypot(point.x - lineStart.x, point.y - lineStart.y);\n }\n const normalLength = Math.hypot(dx, dy);\n const distance = Math.abs(dy * point.x - dx * point.y + lineEnd.x * lineStart.y - lineEnd.y * lineStart.x) / normalLength;\n return distance;\n }\n _createOverlayCanvas() {\n const doc = this._recorder.injectedScript.document;\n this._overlayCanvas = doc.createElementNS(\"http://www.w3.org/2000/svg\", \"svg\");\n this._overlayCanvas.classList.add(\"pw-deletion-trail\");\n this._overlayCanvas.style.position = \"fixed\";\n this._overlayCanvas.style.top = \"0\";\n this._overlayCanvas.style.left = \"0\";\n this._overlayCanvas.style.width = \"100%\";\n this._overlayCanvas.style.height = \"100%\";\n this._overlayCanvas.style.pointerEvents = \"none\";\n this._overlayCanvas.style.zIndex = \"2147483646\";\n this._overlayPath = doc.createElementNS(\"http://www.w3.org/2000/svg\", \"path\");\n this._overlayPath.classList.add(\"pw-deletion-path\");\n this._overlayPath.setAttribute(\"stroke\", \"rgba(220, 53, 69, 0.5)\");\n this._overlayPath.setAttribute(\"stroke-width\", \"20\");\n this._overlayPath.setAttribute(\"stroke-linecap\", \"round\");\n this._overlayPath.setAttribute(\"stroke-linejoin\", \"round\");\n this._overlayPath.setAttribute(\"fill\", \"none\");\n this._overlayCanvas.appendChild(this._overlayPath);\n doc.body.appendChild(this._overlayCanvas);\n }\n _removeOverlayCanvas() {\n if (this._overlayCanvas) {\n this._overlayCanvas.remove();\n this._overlayCanvas = void 0;\n this._overlayPath = void 0;\n }\n }\n _updateOverlayPath() {\n if (!this._overlayPath || this._viewportPath.length < 2)\n return;\n const d = this._viewportPath.reduce((path, point, index) => {\n const command = index === 0 ? \"M\" : \"L\";\n return `${path} ${command}${point.x},${point.y}`;\n }, \"\");\n this._overlayPath.setAttribute(\"d\", d);\n }\n _clearOverlayPath() {\n if (this._overlayPath)\n this._overlayPath.setAttribute(\"d\", \"\");\n }\n _dispatchRealMouseEvent(type, pointerEvent, target) {\n const mouseEvent = new MouseEvent(type, {\n bubbles: true,\n cancelable: true,\n view: this._recorder.injectedScript.window,\n detail: pointerEvent.detail,\n screenX: pointerEvent.screenX,\n screenY: pointerEvent.screenY,\n clientX: pointerEvent.clientX,\n clientY: pointerEvent.clientY,\n ctrlKey: pointerEvent.ctrlKey,\n altKey: pointerEvent.altKey,\n shiftKey: pointerEvent.shiftKey,\n metaKey: pointerEvent.metaKey,\n button: pointerEvent.button,\n buttons: pointerEvent.buttons,\n relatedTarget: pointerEvent.relatedTarget\n });\n target.dispatchEvent(mouseEvent);\n }\n};\n\n// packages/injected/src/recorder/skyramp/tableSnapshotTool.ts\nfunction consumeEvent2(e) {\n e.preventDefault();\n e.stopPropagation();\n e.stopImmediatePropagation();\n}\nfunction getTimestamp5(recorder) {\n return recorder.injectedScript.utils.builtins.Date.now().toString();\n}\nvar TableSnapshotTool = class {\n constructor(recorder) {\n this._hoveredTable = null;\n this._highlightModel = null;\n this._captured = false;\n this._recorder = recorder;\n }\n cursor() {\n return \"crosshair\";\n }\n install() {\n var _a;\n (_a = this._recorder.injectedScript.document.body) == null ? void 0 : _a.setAttribute(\"data-pw-cursor\", \"crosshair\");\n this._captured = false;\n }\n uninstall() {\n this._hoveredTable = null;\n this._highlightModel = null;\n this._captured = false;\n this._recorder.clearHighlight();\n }\n onKeyDown(event) {\n if (this._captured)\n return;\n if (event.key === \"Escape\") {\n consumeEvent2(event);\n this._hoveredTable = null;\n this._highlightModel = null;\n this._recorder.clearHighlight();\n this._recorder.setMode(\"recording\");\n }\n }\n onMouseMove(event) {\n if (this._captured) return;\n consumeEvent2(event);\n const target = this._findTableFromEvent(event);\n if (target !== this._hoveredTable) {\n this._hoveredTable = target;\n this._updateHighlight(target);\n }\n }\n onMouseEnter(event) {\n if (this._captured) return;\n consumeEvent2(event);\n }\n onMouseLeave(event) {\n if (this._captured) return;\n consumeEvent2(event);\n const window2 = this._recorder.injectedScript.window;\n if (window2.top !== window2 && this._recorder.deepEventTarget(event).nodeType === Node.DOCUMENT_NODE) {\n this._hoveredTable = null;\n this._highlightModel = null;\n this._recorder.clearHighlight();\n }\n }\n onClick(event) {\n if (this._captured) return;\n if (event.button !== 0) {\n consumeEvent2(event);\n return;\n }\n if (this._hoveredTable) {\n consumeEvent2(event);\n this._captureTableSnapshot(this._hoveredTable);\n }\n }\n onPointerDown(event) {\n if (this._captured) return;\n consumeEvent2(event);\n }\n onPointerUp(event) {\n if (this._captured) return;\n consumeEvent2(event);\n }\n onMouseDown(event) {\n if (this._captured) return;\n consumeEvent2(event);\n }\n onMouseUp(event) {\n if (this._captured) return;\n consumeEvent2(event);\n }\n _findTableFromEvent(event) {\n let element = this._recorder.deepEventTarget(event);\n while (element) {\n if (element.tagName === \"TABLE\") {\n return element;\n }\n element = element.parentElement;\n }\n return null;\n }\n _updateHighlight(table) {\n if (!table) {\n this._recorder.clearHighlight();\n return;\n }\n const generated = this._recorder.injectedScript.generateSelector(table, {\n testIdAttributeName: this._recorder.state.testIdAttributeName,\n multiple: false\n });\n this._highlightModel = {\n selector: generated.selector,\n elements: generated.elements,\n tooltipText: \"Click to assert table cell\",\n color: \"#4CAF5080\"\n // Green with transparency\n };\n this._recorder.updateHighlight(this._highlightModel, true);\n }\n _captureTableSnapshot(table) {\n var _a;\n const snapshot = this._extractTableData(table);\n const generated = this._recorder.injectedScript.generateSelector(table, {\n testIdAttributeName: this._recorder.state.testIdAttributeName\n });\n const action = {\n name: \"tableSnapshot\",\n selector: generated.selector,\n tableData: snapshot,\n signals: [],\n timestamp: getTimestamp5(this._recorder)\n };\n this._recorder.recordAction(action);\n this._captured = true;\n this._recorder.clearHighlight();\n this._recorder.setMode(\"recording\");\n (_a = this._recorder.overlay) == null ? void 0 : _a.flashToolSucceeded(\"recordingTableSnapshot\");\n }\n _extractTableData(table) {\n const data = {\n headers: [],\n rows: [],\n metadata: {\n rowCount: 0,\n columnCount: 0,\n hasHeaders: false,\n captureTime: (/* @__PURE__ */ new Date()).toISOString()\n }\n };\n const thead = table.querySelector(\"thead\");\n if (thead) {\n const headerRow = thead.querySelector(\"tr\");\n if (headerRow) {\n data.headers = Array.from(headerRow.querySelectorAll(\"th, td\")).map((cell) => this._getCellText(cell));\n data.metadata.hasHeaders = true;\n }\n }\n const tbody = table.querySelector(\"tbody\") || table;\n const bodyRows = tbody.querySelectorAll(\"tr\");\n data.rows = Array.from(bodyRows).map((row) => {\n return Array.from(row.querySelectorAll(\"th, td\")).map((cell) => ({\n text: this._getCellText(cell),\n isHeader: cell.tagName === \"TH\"\n }));\n });\n data.metadata.rowCount = data.rows.length;\n data.metadata.columnCount = Math.max(\n data.headers.length,\n ...data.rows.map((row) => row.length)\n );\n return data;\n }\n _getCellText(cell) {\n var _a;\n return ((_a = cell.innerText) == null ? void 0 : _a.trim()) || \"\";\n }\n};\n\n// packages/injected/src/recorder/skyramp/tableSelectorBuilder.ts\nfunction escapeTextIs(value) {\n return value.replace(/\\\\/g, \"\\\\\\\\\").replace(/\"/g, '\\\\\"');\n}\nfunction buildRowSegment(input) {\n const key = input.rowKey;\n if (key && key.value) {\n const v = escapeTextIs(key.value);\n const keyCell = `${key.tag}:nth-child(${key.colIndex + 1}):is(:text-is(\"${v}\"), :has(:text-is(\"${v}\")))`;\n return `tr:has(${keyCell})`;\n }\n return `tr:nth-child(${input.rowIndex + 1})`;\n}\nfunction buildTableCellSelector(input) {\n const rowSegment = buildRowSegment(input);\n const cellSegment = `${input.cellTag}:nth-child(${input.colIndex + 1})`;\n const core = `tbody ${rowSegment} ${cellSegment}`;\n let selector = input.tablePrefix ? `${input.tablePrefix} ${core}` : core;\n if (input.isInput)\n selector += \" input\";\n return selector;\n}\n\n// packages/injected/src/recorder/skyramp/tableAssertTool.ts\nfunction getTimestamp6(recorder) {\n return recorder.injectedScript.utils.builtins.Date.now().toString();\n}\nvar TableAssertTool = class {\n constructor(recorder) {\n this._highlightedCell = null;\n this._cellHighlight = null;\n this._assertModal = null;\n this._recorder = recorder;\n }\n cursor() {\n return \"pointer\";\n }\n install() {\n var _a;\n (_a = this._recorder.injectedScript.document.body) == null ? void 0 : _a.setAttribute(\"data-pw-cursor\", \"pointer\");\n }\n uninstall() {\n this._removeHighlight();\n this._removeModal();\n }\n cleanup() {\n this.uninstall();\n }\n onPointerMove(event) {\n const cell = this._getCellUnderPointer(event);\n if (cell && cell !== this._highlightedCell) {\n this._highlightedCell = cell;\n this._showCellHighlight(cell, true);\n } else if (!cell && this._highlightedCell) {\n this._removeHighlight();\n this._highlightedCell = null;\n }\n }\n onPointerDown(event) {\n const cell = this._getCellUnderPointer(event);\n if (!cell)\n return;\n event.preventDefault();\n event.stopPropagation();\n this._showCellHighlight(cell, false);\n this._showAssertModal(cell);\n }\n // Helper: Get table cell under pointer\n _getCellUnderPointer(event) {\n const target = event.target;\n return this._isTableCell(target) ? target : target.closest(\"td, th\");\n }\n // Helper: Check if element is a table cell\n _isTableCell(element) {\n if (!element)\n return false;\n const tagName = element.tagName.toLowerCase();\n return tagName === \"td\" || tagName === \"th\";\n }\n // Helper: Find parent table\n _findTable(cell) {\n return cell.closest(\"table\");\n }\n // Helper: Get cell position (row, col)\n _getCellPosition(cell) {\n const row = cell.closest(\"tr\");\n const table = this._findTable(cell);\n if (!row || !table)\n return { row: 0, col: 0 };\n const tbody = table.querySelector(\"tbody\");\n const rows = tbody ? Array.from(tbody.querySelectorAll(\"tr\")) : Array.from(table.querySelectorAll(\"tr\"));\n const rowIndex = rows.indexOf(row);\n const cells = Array.from(row.querySelectorAll(\"td, th\"));\n const colIndex = cells.indexOf(cell);\n return { row: rowIndex, col: colIndex };\n }\n // Helper: pick a stable identifying cell for the row (SKYR-3800).\n // Canonical row-key rule (kept identical to the NL/MCP path in\n // traceRecordingBackend._handleAssertTableCell for byte-identical JSONL):\n // the first non-empty cell whose text is not a bare integer — so a leading\n // row-number <th> \"gutter\" is skipped in favour of a real data value — and\n // only when that value is unique within the table. Returns null otherwise, so\n // the caller falls back to the ordinal row position.\n _getRowKey(table, row) {\n const cells = Array.from(row.querySelectorAll(\"td, th\"));\n const cellText = (el) => {\n var _a;\n return ((_a = el.innerText) == null ? void 0 : _a.trim()) || \"\";\n };\n const isBareInt = (s) => /^\\d+$/.test(s);\n let keyIndex = cells.findIndex((c) => cellText(c) !== \"\" && !isBareInt(cellText(c)));\n if (keyIndex === -1)\n keyIndex = cells.findIndex((c) => cellText(c) !== \"\");\n if (keyIndex === -1)\n return null;\n const keyCell = cells[keyIndex];\n const value = cellText(keyCell);\n const tbody = table.querySelector(\"tbody\");\n const bodyRows = tbody ? Array.from(tbody.querySelectorAll(\"tr\")) : Array.from(table.querySelectorAll(\"tr\"));\n const matches = bodyRows.filter((r) => {\n const c = Array.from(r.querySelectorAll(\"td, th\"))[keyIndex];\n return c && cellText(c) === value;\n });\n if (matches.length !== 1)\n return null;\n return { tag: keyCell.tagName.toLowerCase(), colIndex: keyIndex, value };\n }\n // Show cell highlight overlay\n _showCellHighlight(cell, isPreview) {\n this._removeHighlight();\n const doc = this._recorder.injectedScript.document;\n const bounds = cell.getBoundingClientRect();\n this._cellHighlight = doc.createElement(\"div\");\n this._cellHighlight.style.cssText = `\n position: fixed;\n left: ${bounds.left}px;\n top: ${bounds.top}px;\n width: ${bounds.width}px;\n height: ${bounds.height}px;\n outline: 2px ${isPreview ? \"dashed\" : \"solid\"} #4285f4;\n outline-offset: -2px;\n background-color: rgba(66, 133, 244, ${isPreview ? 0.05 : 0.15});\n pointer-events: none;\n z-index: 2147483646;\n transition: all 0.15s ease;\n `;\n if (!isPreview) {\n const checkmark = doc.createElement(\"div\");\n checkmark.textContent = \"\\u2713\";\n checkmark.style.cssText = `\n position: absolute;\n top: 2px;\n right: 2px;\n font-size: 14px;\n color: #4285f4;\n font-weight: bold;\n `;\n this._cellHighlight.appendChild(checkmark);\n }\n doc.body.appendChild(this._cellHighlight);\n }\n // Remove cell highlight\n _removeHighlight() {\n if (this._cellHighlight) {\n this._cellHighlight.remove();\n this._cellHighlight = null;\n }\n }\n // Show assertion modal\n _showAssertModal(cell) {\n var _a;\n this._removeModal();\n const doc = this._recorder.injectedScript.document;\n let cellText = \"\";\n const inputElement = cell.querySelector(\"input\");\n if (inputElement) {\n cellText = inputElement.value || \"\";\n } else {\n cellText = ((_a = cell.innerText) == null ? void 0 : _a.trim()) || \"\";\n }\n const position = this._getCellPosition(cell);\n const backdrop = doc.createElement(\"div\");\n backdrop.style.cssText = `\n position: fixed;\n top: 0;\n left: 0;\n width: 100%;\n height: 100%;\n background: rgba(0, 0, 0, 0.5);\n z-index: 2147483646;\n display: flex;\n align-items: center;\n justify-content: center;\n `;\n const modal = doc.createElement(\"div\");\n modal.style.cssText = `\n background: white;\n border-radius: 8px;\n padding: 24px;\n box-shadow: 0 8px 32px rgba(0, 0, 0, 0.3);\n max-width: 500px;\n min-width: 400px;\n font-family: system-ui, -apple-system, sans-serif;\n `;\n modal.innerHTML = `\n <div style=\"display: flex; justify-content: space-between; align-items: center; margin-bottom: 16px;\">\n <h3 style=\"margin: 0; font-size: 18px; font-weight: 600; color: #202124;\">Assert Cell Value</h3>\n <button id=\"pw-modal-close\" style=\"border: none; background: none; font-size: 24px; cursor: pointer; color: #5f6368; padding: 0; width: 24px; height: 24px; line-height: 24px;\">&times;</button>\n </div>\n <div style=\"margin-bottom: 16px;\">\n <div style=\"font-size: 13px; color: #5f6368; margin-bottom: 4px;\">Cell: Row ${position.row + 1}, Column ${position.col + 1}</div>\n <div style=\"font-size: 13px; color: #5f6368; margin-bottom: 12px;\">Current Value: \"${cellText}\"</div>\n </div>\n <div style=\"margin-bottom: 16px;\">\n <label style=\"display: block; font-size: 14px; font-weight: 500; color: #202124; margin-bottom: 8px;\">Expected Value:</label>\n <input\n id=\"pw-expected-value\"\n type=\"text\"\n value=\"${cellText.replace(/\"/g, \"&quot;\")}\"\n style=\"width: 100%; padding: 10px 12px; border: 1px solid #dadce0; border-radius: 4px; font-size: 14px; box-sizing: border-box;\"\n placeholder=\"Enter expected text...\"\n />\n </div>\n <div style=\"display: flex; justify-content: flex-end; gap: 12px; margin-top: 24px;\">\n <button id=\"pw-modal-cancel\" style=\"padding: 8px 16px; border: 1px solid #dadce0; background: white; color: #1a73e8; border-radius: 4px; cursor: pointer; font-size: 14px; font-weight: 500;\">Cancel</button>\n <button id=\"pw-modal-confirm\" style=\"padding: 8px 16px; border: none; background: #1a73e8; color: white; border-radius: 4px; cursor: pointer; font-size: 14px; font-weight: 500;\">Add Assertion</button>\n </div>\n `;\n backdrop.appendChild(modal);\n doc.body.appendChild(backdrop);\n this._assertModal = backdrop;\n const input = modal.querySelector(\"#pw-expected-value\");\n input == null ? void 0 : input.focus();\n input == null ? void 0 : input.select();\n const closeBtn = modal.querySelector(\"#pw-modal-close\");\n const cancelBtn = modal.querySelector(\"#pw-modal-cancel\");\n const confirmBtn = modal.querySelector(\"#pw-modal-confirm\");\n const onClose = () => {\n this._removeModal();\n this._removeHighlight();\n this._recorder.setMode(\"recording\");\n };\n const onConfirm = () => {\n const expectedValue = (input == null ? void 0 : input.value) || cellText;\n this._generateAssertion(cell, expectedValue);\n onClose();\n };\n closeBtn == null ? void 0 : closeBtn.addEventListener(\"click\", onClose);\n cancelBtn == null ? void 0 : cancelBtn.addEventListener(\"click\", onClose);\n confirmBtn == null ? void 0 : confirmBtn.addEventListener(\"click\", onConfirm);\n input == null ? void 0 : input.addEventListener(\"keydown\", (e) => {\n if (e.key === \"Enter\") {\n e.preventDefault();\n onConfirm();\n } else if (e.key === \"Escape\") {\n e.preventDefault();\n onClose();\n }\n });\n backdrop.addEventListener(\"click\", (e) => {\n if (e.target === backdrop)\n onClose();\n });\n }\n // Remove modal\n _removeModal() {\n if (this._assertModal) {\n this._assertModal.remove();\n this._assertModal = null;\n }\n }\n // Generate and record assertion\n _generateAssertion(cell, expectedValue) {\n var _a;\n const table = this._findTable(cell);\n if (!table) {\n console.log(\"[TableAssertTool] No table found for cell\");\n return;\n }\n const position = this._getCellPosition(cell);\n console.log(\"[TableAssertTool] Cell position:\", position);\n const inputElement = cell.querySelector(\"input\");\n const isInput = !!inputElement;\n const tableTestId = table.getAttribute(`data-${this._recorder.state.testIdAttributeName}`) || table.getAttribute(\"data-testid\");\n const tableId = table.id;\n let tablePrefix = \"\";\n if (tableTestId)\n tablePrefix = `[data-testid=\"${tableTestId}\"]`;\n else if (tableId)\n tablePrefix = `#${tableId}`;\n const row = cell.closest(\"tr\");\n const rowKey = row ? this._getRowKey(table, row) : null;\n const cellSelector = buildTableCellSelector({\n tablePrefix,\n cellTag: cell.tagName.toLowerCase(),\n colIndex: position.col,\n rowIndex: position.row,\n rowKey,\n isInput\n });\n console.log(\"[TableAssertTool] Generated selector:\", cellSelector, \"isInput:\", isInput, \"rowKey:\", rowKey == null ? void 0 : rowKey.value);\n const action = {\n name: \"assertTableCell\",\n selector: cellSelector,\n text: expectedValue,\n position,\n isInput,\n signals: [],\n timestamp: getTimestamp6(this._recorder)\n };\n console.log(\"[TableAssertTool] Recording action:\", action);\n this._recorder.recordAction(action);\n (_a = this._recorder.overlay) == null ? void 0 : _a.flashToolSucceeded(\"assertingTableCell\");\n }\n};\n\n// packages/injected/src/recorder/skyramp/visualSnapshotTool.ts\nvar HighlightColors = {\n snapshot: \"#9c7fe480\"\n // Purple for visual snapshots\n};\nfunction consumeEvent3(e) {\n e.preventDefault();\n e.stopPropagation();\n e.stopImmediatePropagation();\n}\nfunction getTimestamp7(recorder) {\n return recorder.injectedScript.utils.builtins.Date.now().toString();\n}\nfunction addEventListener3(target, eventName, listener, useCapture) {\n target.addEventListener(eventName, listener, useCapture);\n return () => target.removeEventListener(eventName, listener, useCapture);\n}\nvar VisualSnapshotTool = class _VisualSnapshotTool {\n constructor(recorder) {\n this._glassOverlay = null;\n this._marquee = null;\n this._dragStart = null;\n this._dragCurrent = null;\n this._isDragging = false;\n this._hoverHighlight = null;\n this._listeners = [];\n this._syntheticHighlightEl = null;\n // Constants\n this.DOUBLE_TOGGLE_TIMEOUT = 1500;\n // 1.5 seconds\n this.DRAG_THRESHOLD = 8;\n // pixels\n this.VIEWPORT_THRESHOLD = 0.8;\n this._recorder = recorder;\n }\n // 80% of viewport\n static async getNextCounter(recorder, type) {\n try {\n if (typeof recorder.injectedScript.window.__pw_recorderIncrementCounter === \"function\") {\n return await recorder.injectedScript.window.__pw_recorderIncrementCounter(type);\n }\n } catch (e) {\n console.error(\"Failed to get counter from server:\", e);\n }\n return Date.now() % 1e3;\n }\n cursor() {\n return this._isDragging ? \"crosshair\" : \"pointer\";\n }\n install() {\n var _a;\n this._createGlassOverlay();\n (_a = this._recorder.injectedScript.document.body) == null ? void 0 : _a.setAttribute(\"data-pw-cursor\", \"pointer\");\n }\n uninstall() {\n var _a;\n this._removeGlassOverlay();\n this._removeMarquee();\n this._cleanup();\n (_a = this._recorder.injectedScript.document.body) == null ? void 0 : _a.removeAttribute(\"data-pw-cursor\");\n }\n cleanup() {\n this._cleanup();\n }\n _cleanup() {\n this._listeners.forEach((remove) => remove());\n this._listeners = [];\n if (this._hoverHighlight && this._recorder) {\n this._recorder.updateHighlight(null, false);\n this._hoverHighlight = null;\n }\n this._cleanupSyntheticHighlight();\n this._dragStart = null;\n this._dragCurrent = null;\n this._isDragging = false;\n }\n _cleanupSyntheticHighlight() {\n if (this._syntheticHighlightEl) {\n this._syntheticHighlightEl.remove();\n this._syntheticHighlightEl = null;\n }\n }\n onKeyDown(event) {\n if (event.key === \"Escape\") {\n consumeEvent3(event);\n this._cancelSnapshot();\n }\n }\n _createGlassOverlay() {\n const doc = this._recorder.injectedScript.document;\n this._glassOverlay = doc.createElement(\"x-pw-glass\");\n this._glassOverlay.style.cssText = `\n position: fixed !important;\n top: 0 !important;\n left: 0 !important;\n right: 0 !important;\n bottom: 0 !important;\n z-index: 2147483646 !important;\n background: rgba(0, 120, 215, 0.05) !important;\n cursor: pointer !important;\n pointer-events: auto !important;\n `;\n this._listeners.push(addEventListener3(this._glassOverlay, \"pointerdown\", (e) => this._onGlassPointerDown(e), true));\n this._listeners.push(addEventListener3(this._glassOverlay, \"pointermove\", (e) => this._onGlassPointerMove(e), true));\n this._listeners.push(addEventListener3(this._glassOverlay, \"pointerup\", (e) => this._onGlassPointerUp(e), true));\n this._listeners.push(addEventListener3(this._glassOverlay, \"click\", (e) => consumeEvent3(e), true));\n if (doc.body)\n doc.body.appendChild(this._glassOverlay);\n }\n _removeGlassOverlay() {\n if (this._glassOverlay) {\n this._glassOverlay.remove();\n this._glassOverlay = null;\n }\n }\n _createMarquee() {\n if (this._marquee)\n return;\n const doc = this._recorder.injectedScript.document;\n this._marquee = doc.createElement(\"x-pw-marquee\");\n this._marquee.style.cssText = `\n position: fixed !important;\n border: 2px dashed #0078d7 !important;\n background: rgba(0, 120, 215, 0.1) !important;\n z-index: 2147483647 !important;\n pointer-events: none !important;\n `;\n doc.body.appendChild(this._marquee);\n }\n _updateMarquee() {\n if (!this._marquee || !this._dragStart || !this._dragCurrent)\n return;\n const x1 = Math.min(this._dragStart.x, this._dragCurrent.x);\n const y1 = Math.min(this._dragStart.y, this._dragCurrent.y);\n const x2 = Math.max(this._dragStart.x, this._dragCurrent.x);\n const y2 = Math.max(this._dragStart.y, this._dragCurrent.y);\n this._marquee.style.left = x1 + \"px\";\n this._marquee.style.top = y1 + \"px\";\n this._marquee.style.width = x2 - x1 + \"px\";\n this._marquee.style.height = y2 - y1 + \"px\";\n }\n _removeMarquee() {\n if (this._marquee) {\n this._marquee.remove();\n this._marquee = null;\n }\n }\n _onGlassPointerDown(event) {\n consumeEvent3(event);\n this._dragStart = { x: event.clientX, y: event.clientY };\n this._dragCurrent = this._dragStart;\n }\n _onGlassPointerMove(event) {\n consumeEvent3(event);\n if (!this._dragStart) {\n this._updateHoverHighlight(event);\n return;\n }\n this._dragCurrent = { x: event.clientX, y: event.clientY };\n const distance = Math.hypot(\n this._dragCurrent.x - this._dragStart.x,\n this._dragCurrent.y - this._dragStart.y\n );\n if (distance >= this.DRAG_THRESHOLD && !this._isDragging) {\n this._isDragging = true;\n this._createMarquee();\n if (this._glassOverlay)\n this._glassOverlay.style.cursor = \"crosshair\";\n }\n if (this._isDragging) {\n this._updateMarquee();\n } else {\n this._updateHoverHighlight(event);\n }\n }\n async _onGlassPointerUp(event) {\n consumeEvent3(event);\n if (this._isDragging) {\n await this._captureRegionSnapshot();\n } else if (this._dragStart) {\n await this._captureClickSnapshot(event);\n }\n this._recorder.setMode(\"recording\");\n }\n _updateHoverHighlight(event) {\n var _a, _b, _c, _d;\n if (!this._recorder)\n return;\n if (this._glassOverlay)\n this._glassOverlay.style.display = \"none\";\n const rawTarget = this._recorder.document.elementFromPoint(event.clientX, event.clientY);\n if (this._glassOverlay)\n this._glassOverlay.style.display = \"\";\n if (!rawTarget)\n return;\n if (((_a = rawTarget.tagName) == null ? void 0 : _a.toLowerCase()) === \"iframe\") {\n const iframe = rawTarget;\n try {\n const iframeRect = iframe.getBoundingClientRect();\n const iframeDoc = iframe.contentDocument;\n if (iframeDoc) {\n const initialChain = [{ iframe, selector: this._generateStableSelector(iframe) }];\n const localX = event.clientX - iframeRect.left;\n const localY = event.clientY - iframeRect.top;\n const gojs = this._findGoJSDiagramRecursive(\n initialChain,\n iframeDoc,\n localX,\n localY,\n iframeRect.left,\n iframeRect.top\n );\n if (gojs) {\n const containerRect = gojs.containerEl.getBoundingClientRect();\n const mainLeft = gojs.accOffsetX + containerRect.left;\n const mainTop = gojs.accOffsetY + containerRect.top;\n if (this._syntheticHighlightEl) {\n const s = this._syntheticHighlightEl.style;\n if (s.left === `${mainLeft}px` && s.top === `${mainTop}px`)\n return;\n }\n this._cleanupSyntheticHighlight();\n const doc = this._recorder.document;\n const synth = doc.createElement(\"x-pw-gojs-highlight\");\n synth.style.cssText = [\n \"position: fixed\",\n \"pointer-events: none\",\n \"z-index: -1\",\n `left: ${mainLeft}px`,\n `top: ${mainTop}px`,\n `width: ${containerRect.width}px`,\n `height: ${containerRect.height}px`\n ].join(\" !important; \") + \" !important;\";\n (_b = doc.body) == null ? void 0 : _b.appendChild(synth);\n this._syntheticHighlightEl = synth;\n const generated2 = this._recorder.injectedScript.generateSelector(iframe, {\n testIdAttributeName: this._recorder.state.testIdAttributeName\n });\n this._hoverHighlight = {\n selector: generated2.selector,\n elements: [synth],\n color: HighlightColors.snapshot,\n tooltipText: \"GoJS diagram (iframe)\"\n };\n this._recorder.updateHighlight(this._hoverHighlight, true);\n return;\n }\n const found = this._findElementInIframeRecursive(\n initialChain,\n iframeDoc,\n localX,\n localY,\n iframeRect.left,\n iframeRect.top\n );\n if (found) {\n const elRect = found.element.getBoundingClientRect();\n const mainLeft = found.accOffsetX + elRect.left;\n const mainTop = found.accOffsetY + elRect.top;\n if (this._syntheticHighlightEl) {\n const s = this._syntheticHighlightEl.style;\n if (s.left === `${mainLeft}px` && s.top === `${mainTop}px`)\n return;\n }\n this._cleanupSyntheticHighlight();\n const doc = this._recorder.document;\n const synth = doc.createElement(\"x-pw-gojs-highlight\");\n synth.style.cssText = [\n \"position: fixed\",\n \"pointer-events: none\",\n \"z-index: -1\",\n `left: ${mainLeft}px`,\n `top: ${mainTop}px`,\n `width: ${elRect.width}px`,\n `height: ${elRect.height}px`\n ].join(\" !important; \") + \" !important;\";\n (_c = doc.body) == null ? void 0 : _c.appendChild(synth);\n this._syntheticHighlightEl = synth;\n const selector = this._generateStableSelector(found.element);\n this._hoverHighlight = {\n selector,\n elements: [synth],\n color: HighlightColors.snapshot,\n tooltipText: \"Element (iframe)\"\n };\n this._recorder.updateHighlight(this._hoverHighlight, true);\n return;\n }\n }\n } catch (e) {\n }\n this._cleanupSyntheticHighlight();\n } else {\n this._cleanupSyntheticHighlight();\n }\n const target = this._resolvePdfTarget(rawTarget) || rawTarget;\n if (((_d = this._hoverHighlight) == null ? void 0 : _d.elements[0]) === target)\n return;\n const generated = this._recorder.injectedScript.generateSelector(target, {\n testIdAttributeName: this._recorder.state.testIdAttributeName\n });\n this._hoverHighlight = {\n selector: generated.selector,\n elements: generated.elements,\n color: HighlightColors.snapshot\n };\n this._recorder.updateHighlight(this._hoverHighlight, true);\n }\n async _captureClickSnapshot(event) {\n var _a;\n const glassWasVisible = this._glassOverlay && this._glassOverlay.style.display !== \"none\";\n if (this._glassOverlay)\n this._glassOverlay.style.display = \"none\";\n const rawTarget = this._recorder.document.elementFromPoint(event.clientX, event.clientY);\n if (this._glassOverlay && glassWasVisible)\n this._glassOverlay.style.display = \"\";\n if (!rawTarget)\n return;\n if (((_a = rawTarget.tagName) == null ? void 0 : _a.toLowerCase()) === \"iframe\") {\n const iframe = rawTarget;\n try {\n const iframeRect = iframe.getBoundingClientRect();\n const iframeDoc = iframe.contentDocument;\n if (iframeDoc) {\n const initialChain = [{ iframe, selector: this._generateStableSelector(iframe) }];\n const localX = event.clientX - iframeRect.left;\n const localY = event.clientY - iframeRect.top;\n const gojs = this._findGoJSDiagramRecursive(\n initialChain,\n iframeDoc,\n localX,\n localY,\n iframeRect.left,\n iframeRect.top\n );\n if (gojs) {\n await this._captureGoJsDiagramSnapshot(gojs.iframeChain, gojs.diagramSelector);\n return;\n }\n const found = this._findElementInIframeRecursive(\n initialChain,\n iframeDoc,\n localX,\n localY,\n iframeRect.left,\n iframeRect.top\n );\n if (found) {\n await this._captureIframeElementSnapshot(found.iframeChain, found.element);\n return;\n }\n }\n } catch (e) {\n }\n await this._captureElementSnapshot(iframe);\n return;\n }\n const forcePageSnapshot = event.altKey;\n const snapParent = event.shiftKey && rawTarget.parentElement;\n const baseTarget = snapParent ? rawTarget.parentElement : rawTarget;\n const pdfPageWrapper = this._resolvePdfTarget(baseTarget);\n const actualTarget = pdfPageWrapper || baseTarget;\n const isPageSnapshot = forcePageSnapshot || !pdfPageWrapper && this._shouldCapturePageSnapshot(actualTarget);\n if (isPageSnapshot) {\n await this._capturePageSnapshot();\n } else {\n await this._captureElementSnapshot(actualTarget);\n }\n }\n /**\n * Resolves a PDF text layer element or its descendant to the containing\n * canvasWrapper (the div with [data-page-number]). Returns null if the\n * element is not inside a PDF page.\n */\n _resolvePdfTarget(element) {\n const wrapper = element.closest(\"[data-page-number]\");\n if (wrapper)\n return wrapper;\n return null;\n }\n _shouldCapturePageSnapshot(element, contextWindow) {\n var _a;\n const tagName = (_a = element.tagName) == null ? void 0 : _a.toLowerCase();\n if (tagName === \"html\" || tagName === \"body\")\n return true;\n const win = contextWindow != null ? contextWindow : this._recorder.injectedScript.window;\n const rect = element.getBoundingClientRect();\n const viewportArea = win.innerWidth * win.innerHeight;\n const elementArea = rect.width * rect.height;\n return elementArea >= viewportArea * this.VIEWPORT_THRESHOLD;\n }\n /**\n * Builds a stable CSS selector for an element using id, data-testid, or\n * nth-child position as fallback — mirrors the logic in dragDropTool._findGoJSContainer.\n */\n _buildSelectorFromEl(el) {\n if (el.id)\n return `#${el.id}`;\n const testId = el.getAttribute(\"data-testid\");\n if (testId)\n return `[data-testid=\"${testId}\"]`;\n const parent = el.parentElement;\n if (parent) {\n const idx = Array.from(parent.children).indexOf(el) + 1;\n return `${el.tagName.toLowerCase()}:nth-child(${idx})`;\n }\n return el.tagName.toLowerCase();\n }\n /**\n * Recursively pierces through nested iframes from a click/hover position,\n * walking up the DOM at each level to find a GoJS diagram container.\n *\n * @param iframeChain Accumulated iframe selector chain (outermost first).\n * @param currentDoc The document to search in at this recursion level.\n * @param localX/localY Click position in currentDoc's own viewport coords.\n * @param accOffsetX/Y Accumulated offset to add to element rects for main-doc coords.\n */\n _findGoJSDiagramRecursive(iframeChain, currentDoc, localX, localY, accOffsetX, accOffsetY) {\n var _a, _b, _c;\n const glassInDoc = currentDoc.querySelector(\"x-pw-glass\");\n if (glassInDoc)\n glassInDoc.style.display = \"none\";\n let innerTarget = null;\n try {\n innerTarget = currentDoc.elementFromPoint(localX, localY);\n } catch (e) {\n if (glassInDoc)\n glassInDoc.style.display = \"\";\n return null;\n }\n if (glassInDoc)\n glassInDoc.style.display = \"\";\n if (!innerTarget)\n return null;\n if (((_a = innerTarget.tagName) == null ? void 0 : _a.toLowerCase()) === \"iframe\") {\n const nested = innerTarget;\n try {\n const nestedDoc = nested.contentDocument;\n if (!nestedDoc)\n return null;\n const nestedRect = nested.getBoundingClientRect();\n return this._findGoJSDiagramRecursive(\n [...iframeChain, { iframe: nested, selector: this._generateStableSelector(nested) }],\n nestedDoc,\n localX - nestedRect.left,\n localY - nestedRect.top,\n accOffsetX + nestedRect.left,\n accOffsetY + nestedRect.top\n );\n } catch (e) {\n return null;\n }\n }\n const win = currentDoc.defaultView;\n if (!((_c = (_b = win == null ? void 0 : win.go) == null ? void 0 : _b.Diagram) == null ? void 0 : _c.fromDiv))\n return null;\n const body = currentDoc.body;\n let el = innerTarget;\n while (el && el !== body) {\n if (win.go.Diagram.fromDiv(el))\n return { containerEl: el, diagramSelector: this._buildSelectorFromEl(el), iframeChain, accOffsetX, accOffsetY };\n el = el.parentElement;\n }\n return null;\n }\n /**\n * Recursively pierces through nested iframes to find the actual element\n * at the given viewport position. Returns the element, its iframe chain,\n * and the iframe document — or null if cross-origin or inaccessible.\n */\n _findElementInIframeRecursive(iframeChain, currentDoc, localX, localY, accOffsetX, accOffsetY) {\n var _a;\n const glass = currentDoc.querySelector(\"x-pw-glass\");\n if (glass)\n glass.style.display = \"none\";\n let innerTarget = null;\n try {\n innerTarget = currentDoc.elementFromPoint(localX, localY);\n } catch (e) {\n if (glass)\n glass.style.display = \"\";\n return null;\n }\n if (glass)\n glass.style.display = \"\";\n if (!innerTarget)\n return null;\n if (((_a = innerTarget.tagName) == null ? void 0 : _a.toLowerCase()) === \"iframe\") {\n const nested = innerTarget;\n try {\n const nestedDoc = nested.contentDocument;\n if (!nestedDoc)\n return null;\n const nestedRect = nested.getBoundingClientRect();\n return this._findElementInIframeRecursive(\n [...iframeChain, { iframe: nested, selector: this._generateStableSelector(nested) }],\n nestedDoc,\n localX - nestedRect.left,\n localY - nestedRect.top,\n accOffsetX + nestedRect.left,\n accOffsetY + nestedRect.top\n );\n } catch (e) {\n return null;\n }\n }\n return { element: innerTarget, iframeChain, document: currentDoc, accOffsetX, accOffsetY };\n }\n /**\n * Generates a stable selector for an element by delegating to the polling\n * recorder of the element's own document (__pw_recorderGenerateSelector\n * exposed by pollingRecorder.ts). This routes through Playwright's full\n * selector machinery (incl. ScopingHandler dynamic-ID filtering), so an\n * iframe with id=\"_commonPopup677_iframe\" yields a stable form like\n * iframe[src*=\"...\"] / iframe[title=\"...\"] / iframe[name=\"...\"] instead\n * of the random ID. Used for both iframe elements (when seeding the\n * iframe chain) and elements found inside an iframe. Falls back to\n * _buildSelectorFromEl when the polling recorder is unavailable\n * (e.g. cross-origin or recorder not yet attached).\n */\n _generateStableSelector(element) {\n var _a;\n try {\n const win = (_a = element.ownerDocument) == null ? void 0 : _a.defaultView;\n if (win == null ? void 0 : win.__pw_recorderGenerateSelector) {\n const generated = win.__pw_recorderGenerateSelector(element, {\n testIdAttributeName: this._recorder.state.testIdAttributeName\n });\n if (generated == null ? void 0 : generated.selector)\n return generated.selector;\n }\n } catch (e) {\n }\n return this._buildSelectorFromEl(element);\n }\n /**\n * Records an element visualSnapshot action for an element found inside\n * one or more iframes, including the iframe selector chain.\n */\n async _captureIframeElementSnapshot(iframeChain, element) {\n var _a;\n const selector = this._generateStableSelector(element);\n const counter = await _VisualSnapshotTool.getNextCounter(this._recorder, \"element\");\n const filename = `el-${String(counter).padStart(3, \"0\")}.png`;\n const action = {\n name: \"visualSnapshot\",\n snapshotType: \"element\",\n iframeSelectors: iframeChain.map((item) => item.selector),\n selector,\n filename,\n signals: [],\n timestamp: getTimestamp7(this._recorder)\n };\n this._recorder.recordAction(action);\n (_a = this._recorder.overlay) == null ? void 0 : _a.flashToolSucceeded(\"assertingVSnapshot\");\n }\n /**\n * Records a gojsDiagram visualSnapshot action for a GoJS canvas found inside\n * one or more nested iframes.\n */\n async _captureGoJsDiagramSnapshot(iframeChain, diagramSelector) {\n var _a;\n const counter = await _VisualSnapshotTool.getNextCounter(this._recorder, \"element\");\n const filename = `gojs-${String(counter).padStart(3, \"0\")}.png`;\n const action = {\n name: \"visualSnapshot\",\n snapshotType: \"gojsDiagram\",\n iframeSelectors: iframeChain.map((item) => item.selector),\n diagramSelector,\n filename,\n signals: [],\n timestamp: getTimestamp7(this._recorder)\n };\n this._recorder.recordAction(action);\n (_a = this._recorder.overlay) == null ? void 0 : _a.flashToolSucceeded(\"assertingVSnapshot\");\n }\n async _capturePageSnapshot() {\n var _a;\n const counter = await _VisualSnapshotTool.getNextCounter(this._recorder, \"page\");\n const filename = `page-${String(counter).padStart(3, \"0\")}.png`;\n const action = {\n name: \"visualSnapshot\",\n snapshotType: \"page\",\n filename,\n fullPage: true,\n signals: [],\n timestamp: getTimestamp7(this._recorder)\n };\n this._recorder.recordAction(action);\n (_a = this._recorder.overlay) == null ? void 0 : _a.flashToolSucceeded(\"assertingVSnapshot\");\n }\n async _captureElementSnapshot(element) {\n var _a;\n const generated = this._recorder.injectedScript.generateSelector(element, {\n testIdAttributeName: this._recorder.state.testIdAttributeName\n });\n const counter = await _VisualSnapshotTool.getNextCounter(this._recorder, \"element\");\n const filename = `el-${String(counter).padStart(3, \"0\")}.png`;\n const action = {\n name: \"visualSnapshot\",\n snapshotType: \"element\",\n selector: generated.selector,\n filename,\n signals: [],\n timestamp: getTimestamp7(this._recorder)\n };\n this._recorder.recordAction(action);\n (_a = this._recorder.overlay) == null ? void 0 : _a.flashToolSucceeded(\"assertingVSnapshot\");\n }\n async _captureRegionSnapshot() {\n var _a;\n if (!this._dragStart || !this._dragCurrent)\n return;\n const x1 = Math.min(this._dragStart.x, this._dragCurrent.x);\n const y1 = Math.min(this._dragStart.y, this._dragCurrent.y);\n const x2 = Math.max(this._dragStart.x, this._dragCurrent.x);\n const y2 = Math.max(this._dragStart.y, this._dragCurrent.y);\n const scrollX = this._recorder.injectedScript.window.scrollX || this._recorder.injectedScript.window.pageXOffset;\n const scrollY = this._recorder.injectedScript.window.scrollY || this._recorder.injectedScript.window.pageYOffset;\n const clip = {\n x: Math.round(x1 + scrollX),\n y: Math.round(y1 + scrollY),\n width: Math.round(x2 - x1),\n height: Math.round(y2 - y1)\n };\n const counter = await _VisualSnapshotTool.getNextCounter(this._recorder, \"region\");\n const filename = `region-${String(counter).padStart(3, \"0\")}.png`;\n const action = {\n name: \"visualSnapshot\",\n snapshotType: \"region\",\n clip,\n filename,\n signals: [],\n timestamp: getTimestamp7(this._recorder)\n };\n this._recorder.recordAction(action);\n (_a = this._recorder.overlay) == null ? void 0 : _a.flashToolSucceeded(\"assertingVSnapshot\");\n }\n _cancelSnapshot() {\n this._recorder.setMode(\"recording\");\n }\n};\n\n// packages/injected/src/recorder/skyramp/areaSelectionTool.ts\nfunction consumeEvent4(e) {\n e.preventDefault();\n e.stopPropagation();\n e.stopImmediatePropagation();\n}\nfunction getTimestamp8(recorder) {\n return recorder.injectedScript.utils.builtins.Date.now().toString();\n}\nfunction addEventListener4(target, eventName, listener, useCapture) {\n target.addEventListener(eventName, listener, useCapture);\n return () => target.removeEventListener(eventName, listener, useCapture);\n}\nfunction removeEventListeners2(listeners) {\n for (const listener of listeners)\n listener();\n listeners.splice(0, listeners.length);\n}\nvar AreaSelectionTool = class {\n constructor(recorder) {\n this._selectionState = null;\n this._overlay = null;\n this._feedbackTooltip = null;\n this._listeners = [];\n this._recorder = recorder;\n this._initializeConfig();\n }\n _initializeConfig() {\n const win = this._recorder.injectedScript.window;\n if (!win.__playwrightAreaSelectionConfig) {\n win.__playwrightAreaSelectionConfig = {\n minDragDistance: 3,\n // Lowered from 5 to 3 pixels for better sensitivity\n showFeedback: true,\n feedbackDuration: 2e3,\n // 2 seconds\n overlayColor: \"#0ea5e9\",\n // Sky blue\n overlayOpacity: 0.1,\n overlayBorderStyle: \"dashed\",\n overlayBorderWidth: 2\n };\n console.log(\"[AreaSelectionTool] Configuration available at window.__playwrightAreaSelectionConfig\");\n console.log(\"[AreaSelectionTool] Adjust minDragDistance (default: 3px) in DevTools to fine-tune sensitivity\");\n }\n }\n _getConfig() {\n return this._recorder.injectedScript.window.__playwrightAreaSelectionConfig;\n }\n cursor() {\n return \"crosshair\";\n }\n install() {\n this._arm();\n }\n uninstall() {\n this._disarm();\n this._removeOverlay();\n this._removeFeedbackTooltip();\n }\n cleanup() {\n if (this._selectionState) {\n this._disarm();\n this._removeOverlay();\n this._removeFeedbackTooltip();\n }\n }\n onKeyDown(event) {\n if (event.key === \"Escape\") {\n consumeEvent4(event);\n this._removeOverlay();\n this._recorder.setMode(\"recording\");\n }\n }\n _arm() {\n var _a;\n this._selectionState = {\n startPoint: null,\n endPoint: null,\n targetCanvas: null,\n isSelecting: false\n };\n (_a = this._recorder.injectedScript.document.body) == null ? void 0 : _a.setAttribute(\"data-pw-cursor\", \"crosshair\");\n this._createOverlay();\n const onPointerDown = (e) => {\n var _a2;\n const pointerEvent = e;\n if (!this._selectionState)\n return;\n const target = pointerEvent.target;\n if (this._isInteractiveElement(target)) {\n console.log(\"[AreaSelectionTool] Ignoring click on interactive element:\", target.tagName, (_a2 = target.textContent) == null ? void 0 : _a2.substring(0, 30));\n return;\n }\n this._selectionState.startPoint = { x: pointerEvent.clientX, y: pointerEvent.clientY };\n const canvas = this._detectCanvasAtPoint({ x: pointerEvent.clientX, y: pointerEvent.clientY });\n if (canvas) {\n this._selectionState.targetCanvas = canvas;\n } else {\n this._selectionState.targetCanvas = null;\n }\n this._selectionState.isSelecting = true;\n };\n const onPointerMove = (e) => {\n const pointerEvent = e;\n if (this._selectionState && this._selectionState.isSelecting && this._selectionState.startPoint) {\n this._selectionState.endPoint = { x: pointerEvent.clientX, y: pointerEvent.clientY };\n this._updateOverlay(this._selectionState.startPoint, this._selectionState.endPoint);\n }\n };\n const onPointerUp = (e) => {\n const pointerEvent = e;\n if (this._selectionState && this._selectionState.isSelecting) {\n this._selectionState.endPoint = { x: pointerEvent.clientX, y: pointerEvent.clientY };\n this._selectionState.isSelecting = false;\n if (this._selectionState.startPoint && this._selectionState.endPoint) {\n const dragDistance = this._calculateDistance(\n this._selectionState.startPoint,\n this._selectionState.endPoint\n );\n const config = this._getConfig();\n const MIN_DRAG_DISTANCE = config.minDragDistance;\n if (dragDistance >= MIN_DRAG_DISTANCE) {\n console.log(\"[AreaSelectionTool] \\u2713 Capturing area selection, drag distance:\", dragDistance.toFixed(1), \"px (threshold:\", MIN_DRAG_DISTANCE, \"px)\");\n this._showSuccessFeedback(dragDistance);\n this._capture();\n this._selectionState.startPoint = null;\n this._selectionState.endPoint = null;\n this._selectionState.targetCanvas = null;\n this._removeOverlay();\n } else {\n console.warn(\"[AreaSelectionTool] \\u2717 Drag too small:\", dragDistance.toFixed(1), \"px (need \\u2265\", MIN_DRAG_DISTANCE, \"px) - staying active for retry\");\n this._showFailureFeedback(dragDistance, MIN_DRAG_DISTANCE);\n this._selectionState.startPoint = null;\n this._selectionState.endPoint = null;\n this._selectionState.targetCanvas = null;\n this._removeOverlay();\n }\n }\n }\n };\n this._listeners.push(\n addEventListener4(this._recorder.document, \"pointerdown\", onPointerDown, false),\n addEventListener4(this._recorder.document, \"pointermove\", onPointerMove, false),\n addEventListener4(this._recorder.document, \"pointerup\", onPointerUp, false)\n );\n }\n _disarm() {\n removeEventListeners2(this._listeners);\n this._listeners = [];\n this._selectionState = null;\n }\n _createOverlay() {\n const config = this._getConfig();\n this._overlay = this._recorder.document.createElement(\"div\");\n const hexToRgba = (hex, opacity) => {\n const r = parseInt(hex.slice(1, 3), 16);\n const g = parseInt(hex.slice(3, 5), 16);\n const b = parseInt(hex.slice(5, 7), 16);\n return `rgba(${r}, ${g}, ${b}, ${opacity})`;\n };\n this._overlay.style.cssText = `\n position: fixed;\n border: ${config.overlayBorderWidth}px ${config.overlayBorderStyle} ${config.overlayColor};\n background: ${hexToRgba(config.overlayColor, config.overlayOpacity)};\n pointer-events: none;\n z-index: 2147483646;\n display: none;\n box-shadow: 0 0 0 1px rgba(0, 0, 0, 0.1);\n transition: opacity 0.15s ease-in-out;\n `;\n this._recorder.document.body.appendChild(this._overlay);\n }\n _updateOverlay(start, end) {\n if (!this._overlay)\n return;\n const left = Math.min(start.x, end.x);\n const top = Math.min(start.y, end.y);\n const width = Math.abs(end.x - start.x);\n const height = Math.abs(end.y - start.y);\n this._overlay.style.left = `${left}px`;\n this._overlay.style.top = `${top}px`;\n this._overlay.style.width = `${width}px`;\n this._overlay.style.height = `${height}px`;\n this._overlay.style.display = \"block\";\n }\n _removeOverlay() {\n if (this._overlay && this._overlay.parentElement) {\n this._overlay.parentElement.removeChild(this._overlay);\n this._overlay = null;\n }\n }\n _detectCanvasAtPoint(point) {\n const element = this._recorder.document.elementFromPoint(point.x, point.y);\n if ((element == null ? void 0 : element.tagName) === \"CANVAS\") {\n return element;\n }\n return null;\n }\n _detectCanvasContext(point) {\n const element = this._recorder.document.elementFromPoint(point.x, point.y);\n if ((element == null ? void 0 : element.tagName) === \"CANVAS\") {\n const canvas = element;\n const rect = canvas.getBoundingClientRect();\n return { canvas, rect };\n }\n return null;\n }\n _isInteractiveElement(element) {\n var _a, _b;\n if (!element)\n return false;\n let current = element;\n while (current && current !== this._recorder.document.body) {\n const tagName = (_a = current.tagName) == null ? void 0 : _a.toLowerCase();\n if ([\"button\", \"a\", \"input\", \"select\", \"textarea\", \"label\"].includes(tagName))\n return true;\n const role = current.getAttribute(\"role\");\n if (role && [\"button\", \"link\", \"menuitem\", \"tab\", \"checkbox\", \"radio\", \"switch\", \"textbox\"].includes(role))\n return true;\n if (current.hasAttribute(\"onclick\") || current.getAttribute(\"data-testid\"))\n return true;\n const className = ((_b = current.className) == null ? void 0 : _b.toString()) || \"\";\n if (className.match(/btn|button|link|clickable|action/i))\n return true;\n current = current.parentElement;\n }\n return false;\n }\n _calculateDistance(start, end) {\n const dx = end.x - start.x;\n const dy = end.y - start.y;\n return Math.sqrt(dx * dx + dy * dy);\n }\n _showSuccessFeedback(distance) {\n const config = this._getConfig();\n if (!config.showFeedback)\n return;\n this._showFeedbackTooltip(\n `\\u2713 Selection captured (${distance.toFixed(1)}px)`,\n \"#10b981\",\n // Green\n config.feedbackDuration\n );\n }\n _showFailureFeedback(distance, threshold) {\n const config = this._getConfig();\n if (!config.showFeedback)\n return;\n this._showFeedbackTooltip(\n `\\u2717 Drag too small: ${distance.toFixed(1)}px (need \\u2265${threshold}px)`,\n \"#ef4444\",\n // Red\n config.feedbackDuration\n );\n }\n _showFeedbackTooltip(message, color, duration) {\n this._removeFeedbackTooltip();\n this._feedbackTooltip = this._recorder.document.createElement(\"div\");\n this._feedbackTooltip.textContent = message;\n this._feedbackTooltip.style.cssText = `\n position: fixed;\n top: 20px;\n left: 50%;\n transform: translateX(-50%);\n background: ${color};\n color: white;\n padding: 12px 24px;\n border-radius: 6px;\n font-family: system-ui, -apple-system, sans-serif;\n font-size: 14px;\n font-weight: 500;\n box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15);\n z-index: 2147483647;\n pointer-events: none;\n animation: pwSlideIn 0.3s ease-out;\n `;\n const style = this._recorder.document.createElement(\"style\");\n style.textContent = `\n @keyframes pwSlideIn {\n from {\n opacity: 0;\n transform: translateX(-50%) translateY(-10px);\n }\n to {\n opacity: 1;\n transform: translateX(-50%) translateY(0);\n }\n }\n @keyframes pwSlideOut {\n from {\n opacity: 1;\n transform: translateX(-50%) translateY(0);\n }\n to {\n opacity: 0;\n transform: translateX(-50%) translateY(-10px);\n }\n }\n `;\n this._recorder.document.head.appendChild(style);\n this._recorder.document.body.appendChild(this._feedbackTooltip);\n setTimeout(() => {\n if (this._feedbackTooltip) {\n this._feedbackTooltip.style.animation = \"pwSlideOut 0.3s ease-in\";\n setTimeout(() => this._removeFeedbackTooltip(), 300);\n }\n }, duration);\n }\n _removeFeedbackTooltip() {\n if (this._feedbackTooltip && this._feedbackTooltip.parentElement) {\n this._feedbackTooltip.parentElement.removeChild(this._feedbackTooltip);\n this._feedbackTooltip = null;\n }\n }\n _getElementsInRect(rect) {\n const elements = [];\n const candidates = this._recorder.document.querySelectorAll(\"*\");\n for (const el of candidates) {\n const bounds = el.getBoundingClientRect();\n if (!(bounds.right < rect.left || bounds.left > rect.right || bounds.bottom < rect.top || bounds.top > rect.bottom)) {\n const tagName = el.tagName.toLowerCase();\n const isInteractive = [\"button\", \"a\", \"input\", \"select\", \"textarea\"].includes(tagName) || el.hasAttribute(\"role\") || el.hasAttribute(\"data-testid\");\n if (isInteractive)\n elements.push(el);\n }\n }\n return elements;\n }\n _capture() {\n var _a, _b;\n const start = this._selectionState.startPoint;\n const end = this._selectionState.endPoint;\n const left = Math.min(start.x, end.x);\n const top = Math.min(start.y, end.y);\n const width = Math.abs(end.x - start.x);\n const height = Math.abs(end.y - start.y);\n const rect = new DOMRect(left, top, width, height);\n let action;\n const canvas = this._selectionState.targetCanvas || ((_a = this._detectCanvasContext({ x: left + width / 2, y: top + height / 2 })) == null ? void 0 : _a.canvas);\n if (canvas) {\n const canvasGenerated = this._recorder.injectedScript.generateSelector(canvas, {\n testIdAttributeName: this._recorder.state.testIdAttributeName\n });\n action = {\n name: \"selectArea\",\n type: \"canvas\",\n startPoint: start,\n endPoint: end,\n canvasSelector: canvasGenerated.selector,\n signals: [],\n timestamp: getTimestamp8(this._recorder)\n };\n } else {\n const elements = this._getElementsInRect(rect);\n const selectors = elements.map((el) => {\n const generated = this._recorder.injectedScript.generateSelector(el, {\n testIdAttributeName: this._recorder.state.testIdAttributeName\n });\n return generated.selector;\n });\n action = {\n name: \"selectArea\",\n type: elements.length > 0 ? \"dom\" : \"hybrid\",\n startPoint: start,\n endPoint: end,\n selectors: selectors.length > 0 ? selectors : void 0,\n signals: [],\n timestamp: getTimestamp8(this._recorder)\n };\n }\n this._recorder.recordAction(action);\n this._recorder.setMode(\"recording\");\n (_b = this._recorder.overlay) == null ? void 0 : _b.flashToolSucceeded(\"recordingArea\");\n }\n};\n\n// packages/injected/src/recorder/skyramp/domSnapshotTool.ts\nfunction getTimestamp9(recorder) {\n return recorder.injectedScript.utils.builtins.Date.now().toString();\n}\nvar DomSnapshotTool = class {\n constructor(recorder) {\n this._actionSequenceCounter = 0;\n this._recorder = recorder;\n }\n cursor() {\n return \"default\";\n }\n install() {\n this._captureDomSnapshot();\n }\n uninstall() {\n }\n cleanup() {\n }\n _captureDomSnapshot() {\n const snapshot = this._serializeDom();\n const action = {\n name: \"domSnapshot\",\n snapshotData: snapshot,\n signals: [],\n timestamp: getTimestamp9(this._recorder)\n };\n this._recorder.recordAction(action);\n this._recorder.setMode(\"recording\");\n }\n _serializeDom() {\n const doc = this._recorder.document;\n const win = this._recorder.injectedScript.window;\n return {\n url: doc.location.href,\n timestamp: Date.now(),\n viewport: {\n width: win.innerWidth,\n height: win.innerHeight\n },\n dom: this._buildDomTree(doc.documentElement),\n metadata: {\n actionSequence: this._actionSequenceCounter++,\n sessionId: `rec-${Date.now()}`\n }\n };\n }\n _buildDomTree(element, depth = 0) {\n var _a, _b;\n if (depth > 50)\n return null;\n const tagName = element.tagName.toLowerCase();\n if (tagName.startsWith(\"x-pw-\"))\n return null;\n const node = {\n tag: tagName,\n attributes: this._getAttributes(element)\n };\n if (element.childNodes.length === 0 || element.childNodes.length === 1 && ((_a = element.firstChild) == null ? void 0 : _a.nodeType) === 3) {\n const text = (_b = element.textContent) == null ? void 0 : _b.trim();\n if (text)\n node.text = text;\n }\n if (element instanceof HTMLInputElement) {\n node.value = element.value;\n node.checked = element.checked;\n node.type = element.type;\n } else if (element instanceof HTMLTextAreaElement) {\n node.value = element.value;\n } else if (element instanceof HTMLSelectElement) {\n node.value = element.value;\n node.selectedOptions = Array.from(element.selectedOptions).map((o) => o.value);\n }\n if (element.shadowRoot) {\n const shadowChildren = Array.from(element.shadowRoot.children).map((child) => this._buildDomTree(child, depth + 1)).filter(Boolean);\n if (shadowChildren.length > 0)\n node.shadowRoot = shadowChildren;\n }\n const children = Array.from(element.children).map((child) => this._buildDomTree(child, depth + 1)).filter(Boolean);\n if (children.length > 0)\n node.children = children;\n return node;\n }\n _getAttributes(element) {\n const attrs = {};\n const priorityAttrs = [\n \"id\",\n \"class\",\n \"name\",\n \"type\",\n \"role\",\n \"aria-label\",\n \"aria-describedby\",\n \"aria-labelledby\",\n \"data-testid\",\n \"data-test-id\",\n \"data-test\",\n \"placeholder\",\n \"title\",\n \"alt\",\n \"href\",\n \"src\",\n \"value\",\n \"for\",\n \"action\",\n \"method\"\n ];\n for (const attr of priorityAttrs) {\n const value = element.getAttribute(attr);\n if (value)\n attrs[attr] = value;\n }\n return attrs;\n }\n};\n\n// packages/injected/src/recorder/skyramp/modalUtils.ts\nvar MIN_BLOCKING_Z_INDEX = 1e3;\nvar MIN_HIGH_PRIORITY_Z_INDEX = 9999;\nfunction hideModalForAssertion() {\n try {\n const modalExists = document.querySelector(\"#modal-root dialog, #modal-root .modal_root\");\n if (modalExists) {\n const script = `\n (() => {\n const S = (window.__pwHideModal__ ||= {});\n const MIN_BLOCKING_Z_INDEX = ${MIN_BLOCKING_Z_INDEX};\n const MIN_HIGH_PRIORITY_Z_INDEX = ${MIN_HIGH_PRIORITY_Z_INDEX};\n\n // Find ALL dialogs in the modal (main modal + any dropdowns)\n const dialogs = Array.from(document.querySelectorAll('#modal-root dialog, #modal-root .modal_root'));\n if (!dialogs.length) return console.warn('No modal dialogs found.');\n\n if (S.hidden) return console.log('Already hidden.');\n\n // Remember initial state for ALL dialogs\n S.dialogs = dialogs.map(dlg => ({\n element: dlg,\n wasModal: typeof HTMLDialogElement !== 'undefined'\n && dlg instanceof HTMLDialogElement\n && dlg.matches(':modal')\n }));\n\n // Prevent the app from reacting to close/cancel while we hide all dialogs\n S.stopper = e => e.stopImmediatePropagation();\n S.dialogs.forEach(({ element }) => {\n element.addEventListener('close', S.stopper, true);\n element.addEventListener('cancel', S.stopper, true);\n });\n\n // Release the top layer without letting the app know for ALL dialogs\n S.dialogs.forEach(({ element, wasModal }) => {\n try {\n if (wasModal && typeof element.close === 'function') element.close('pw-temp-hide');\n else element.removeAttribute('open');\n } catch {}\n });\n\n // Visually/interaction-wise hide the whole modal container\n const root = document.getElementById('modal-root') || S.dialogs[0]?.element.closest('#modal-root') || S.dialogs[0]?.element;\n S.root = root;\n S.prevVis = root.style.visibility;\n S.prevPE = root.style.pointerEvents;\n root.style.visibility = 'hidden';\n root.style.pointerEvents = 'none';\n\n // Common \"page lock\" cleanups (store and undo later)\n S.bodyOverflow = document.body.style.overflow;\n document.body.style.overflow = '';\n\n S.inertEls = Array.from(document.querySelectorAll('[inert]'));\n S.inertEls.forEach(el => el.removeAttribute('inert'));\n\n S.ariaHidden = [];\n Array.from(document.body.children).forEach(el => {\n if (el === root) return;\n const v = el.getAttribute('aria-hidden');\n if (v !== null) { S.ariaHidden.push([el, v]); el.removeAttribute('aria-hidden'); }\n });\n\n // Find and neutralize ALL blocking elements, especially dropdown-related overlays\n S.tempPeNone = [];\n\n // Check multiple points across the screen for blocking elements\n const testPoints = [\n [innerWidth/2, innerHeight/2], // center\n [innerWidth/4, innerHeight/4], // top-left\n [3*innerWidth/4, innerHeight/4], // top-right\n [innerWidth/4, 3*innerHeight/4], // bottom-left\n [3*innerWidth/4, 3*innerHeight/4], // bottom-right\n [innerWidth/2, innerHeight/4], // top-center\n [innerWidth/2, 3*innerHeight/4], // bottom-center\n ];\n\n testPoints.forEach(([x, y]) => {\n const probe = document.elementFromPoint(x, y);\n if (probe && probe !== root && !root.contains(probe) &&\n !probe.tagName?.toLowerCase().startsWith('x-pw-') &&\n probe.id !== 'x-pw-glass' &&\n !S.tempPeNone.includes(probe)) {\n\n const cs = getComputedStyle(probe);\n const isBlocking = (\n // Original full-screen check\n (cs.position === 'fixed' &&\n cs.top === '0px' && cs.left === '0px' && cs.right === '0px' && cs.bottom === '0px') ||\n // Dropdown overlay patterns\n (cs.position === 'fixed' && parseInt(cs.zIndex) > MIN_BLOCKING_Z_INDEX) ||\n (cs.position === 'absolute' && parseInt(cs.zIndex) > MIN_BLOCKING_Z_INDEX) ||\n // Common backdrop patterns\n (cs.position === 'fixed' && cs.inset === '0px') ||\n // Elements that cover significant area\n (cs.position === 'fixed' && cs.width && cs.height &&\n parseInt(cs.width) > innerWidth/2 && parseInt(cs.height) > innerHeight/2)\n );\n\n if (isBlocking) {\n S.tempPeNone.push(probe);\n probe.style.pointerEvents = 'none';\n console.log('Disabled blocking element at', x, y, ':', probe, 'z-index:', cs.zIndex);\n }\n }\n });\n\n // Also scan for high z-index elements that might be blocking\n const highZElements = Array.from(document.querySelectorAll('*')).filter(el => {\n if (el === root || root.contains(el) ||\n el.tagName?.toLowerCase().startsWith('x-pw-') ||\n S.tempPeNone.includes(el)) return false;\n\n const cs = getComputedStyle(el);\n return cs.zIndex && parseInt(cs.zIndex) > MIN_HIGH_PRIORITY_Z_INDEX;\n });\n\n highZElements.forEach(el => {\n S.tempPeNone.push(el);\n el.style.pointerEvents = 'none';\n console.log('Disabled high z-index element:', el, 'z-index:', getComputedStyle(el).zIndex);\n });\n\n S.hidden = true;\n console.log('\\u2705 Modal hidden automatically for text assertion.');\n })();\n `;\n new Function(script)();\n }\n } catch (e) {\n console.warn(\"Failed to auto-hide modal for assertion:\", e);\n }\n}\nfunction showModalAfterAssertion() {\n try {\n const script = `\n (() => {\n const S = window.__pwHideModal__;\n if (!S?.hidden) return console.warn('Nothing to restore.');\n\n const { dialogs, root } = S;\n if (!dialogs?.length || !root || !document.contains(root)) {\n return console.warn('Modal root/dialogs no longer in DOM (app removed it).');\n }\n\n // Make container visible/clickable again\n root.style.visibility = S.prevVis ?? '';\n root.style.pointerEvents = S.prevPE ?? '';\n\n // Bring ALL dialogs back into the top layer (if they were modal)\n dialogs.forEach(({ element, wasModal }) => {\n try {\n if (wasModal && typeof element.showModal === 'function') element.showModal();\n else element.setAttribute('open', '');\n } catch (e) {\n // Fallback: at least show it\n element.setAttribute('open', '');\n }\n });\n\n // Re-apply page locks as they were\n if (S.bodyOverflow !== undefined) document.body.style.overflow = S.bodyOverflow;\n (S.inertEls || []).forEach(el => el.setAttribute('inert', ''));\n (S.ariaHidden || []).forEach(([el, v]) => el.setAttribute('aria-hidden', v));\n (S.tempPeNone || []).forEach(el => el.style.removeProperty('pointer-events'));\n\n // Allow the app to receive close/cancel in the future for ALL dialogs\n if (S.stopper) {\n dialogs.forEach(({ element }) => {\n element.removeEventListener('close', S.stopper, true);\n element.removeEventListener('cancel', S.stopper, true);\n });\n }\n\n S.hidden = false;\n console.log('\\u2705 Modal restored automatically after text assertion.');\n })();\n `;\n new Function(script)();\n } catch (e) {\n console.warn(\"Failed to auto-show modal after assertion:\", e);\n }\n}\n\n// packages/injected/src/recorder/skyramp/modalHandler.ts\nfunction log2(...args) {\n if (typeof window !== \"undefined\" && window.__SKYRAMP_DEBUG__)\n console.log(\"[ModalHandler]\", ...args);\n}\nfunction buildSelector(element) {\n const testId = element.getAttribute(\"data-testid\");\n if (testId)\n return `dialog[data-testid=\"${testId}\"]`;\n if (element.id)\n return `#${element.id}`;\n const ariaLabel = element.getAttribute(\"aria-label\");\n if (ariaLabel)\n return `dialog[aria-label=\"${ariaLabel}\"]`;\n const tag = element.tagName.toLowerCase();\n const cls = element.className;\n if (typeof cls === \"string\" && cls.trim())\n return `${tag}.${cls.trim().split(/\\s+/).join(\".\")}`;\n return tag;\n}\nvar ModalHandler = class {\n constructor(document2) {\n this._observer = null;\n this._enabled = false;\n this._activeModal = null;\n this._document = document2;\n }\n setOnModalOpen(cb) {\n this._onModalOpen = cb;\n }\n setOnModalClose(cb) {\n this._onModalClose = cb;\n }\n isModalOpen() {\n return this._activeModal !== null;\n }\n enable() {\n if (this._enabled)\n return;\n this._enabled = true;\n if (!this._document.body)\n return;\n this._observer = new MutationObserver((mutations) => this._handleMutations(mutations));\n this._observer.observe(this._document.body, {\n childList: true,\n subtree: true,\n attributes: true,\n attributeFilter: [\"open\", \"aria-modal\", \"class\"]\n });\n this._checkExistingModals();\n log2(\"Enabled \\u2014 watching for modal lifecycle events\");\n }\n disable() {\n if (!this._enabled)\n return;\n this._enabled = false;\n if (this._observer) {\n this._observer.disconnect();\n this._observer = null;\n }\n this._activeModal = null;\n log2(\"Disabled\");\n }\n _checkExistingModals() {\n const openDialog = this._document.querySelector(\"dialog[open]\");\n if (openDialog) {\n this._emitOpen(openDialog);\n return;\n }\n const ariaModal = this._document.querySelector('[aria-modal=\"true\"]');\n if (ariaModal) {\n this._emitOpen(ariaModal);\n return;\n }\n const carbonModal = this._document.querySelector(\".cds--modal.is-visible\");\n if (carbonModal) {\n this._emitOpen(carbonModal);\n return;\n }\n }\n _handleMutations(mutations) {\n var _a, _b, _c;\n if (!this._enabled)\n return;\n for (const mutation of mutations) {\n if (mutation.type === \"attributes\") {\n const target = mutation.target;\n if (mutation.attributeName === \"open\" && target.tagName === \"DIALOG\") {\n if (target.hasAttribute(\"open\"))\n this._emitOpen(target);\n else\n this._emitClose(target);\n continue;\n }\n if (mutation.attributeName === \"aria-modal\") {\n if (target.getAttribute(\"aria-modal\") === \"true\")\n this._emitOpen(target);\n else if (this._activeModal === target)\n this._emitClose(target);\n continue;\n }\n if (mutation.attributeName === \"class\" && ((_a = target.classList) == null ? void 0 : _a.contains(\"cds--modal\"))) {\n if (target.classList.contains(\"is-visible\"))\n this._emitOpen(target);\n else if (this._activeModal === target)\n this._emitClose(target);\n continue;\n }\n }\n if (mutation.type === \"childList\") {\n for (const node of mutation.addedNodes) {\n if (!(node instanceof Element))\n continue;\n if (node.tagName === \"DIALOG\" && node.hasAttribute(\"open\"))\n this._emitOpen(node);\n else if (((_b = node.getAttribute) == null ? void 0 : _b.call(node, \"aria-modal\")) === \"true\")\n this._emitOpen(node);\n else if (((_c = node.classList) == null ? void 0 : _c.contains(\"cds--modal\")) && node.classList.contains(\"is-visible\"))\n this._emitOpen(node);\n }\n if (this._activeModal) {\n for (const node of mutation.removedNodes) {\n if (node === this._activeModal || node instanceof Element && node.contains(this._activeModal))\n this._emitClose(this._activeModal);\n }\n }\n }\n }\n }\n _emitOpen(element) {\n var _a;\n if (this._activeModal === element)\n return;\n this._activeModal = element;\n const selector = buildSelector(element);\n log2(\"Modal opened:\", selector);\n (_a = this._onModalOpen) == null ? void 0 : _a.call(this, { selector });\n }\n _emitClose(element) {\n var _a;\n if (this._activeModal !== element)\n return;\n const selector = buildSelector(element);\n this._activeModal = null;\n log2(\"Modal closed:\", selector);\n (_a = this._onModalClose) == null ? void 0 : _a.call(this, { selector });\n }\n};\n\n// packages/injected/src/recorder/skyramp/iframeHandler.ts\nfunction log3(...args) {\n if (typeof window !== \"undefined\" && window.__SKYRAMP_DEBUG__)\n console.log(\"[IframeHandler]\", ...args);\n}\nfunction isDynamicIframeId(id) {\n if (/\\d{2,}[_-][a-zA-Z]/.test(id)) return true;\n if (/[-_]\\d+$/.test(id)) return true;\n if (/^react-aria\\d+/.test(id)) return true;\n if (/^(mui|mat|cdk)-\\d+/.test(id)) return true;\n if (/^\\d+$/.test(id)) return true;\n if (/\\d{4,}/.test(id)) return true;\n if (id.includes(\":\")) return true;\n return false;\n}\nfunction buildIframeSelector(iframe) {\n if (iframe.title)\n return `iframe[title=\"${iframe.title}\"]`;\n if (iframe.name)\n return `iframe[name=\"${iframe.name}\"]`;\n if (iframe.id && !isDynamicIframeId(iframe.id))\n return `#${iframe.id}`;\n if (iframe.src) {\n try {\n const url = new URL(iframe.src);\n if ((url.protocol === \"http:\" || url.protocol === \"https:\") && url.pathname && url.pathname !== \"/\") {\n return `iframe[src*=\"${url.pathname}\"]`;\n }\n } catch {\n }\n return `iframe[src=\"${iframe.src}\"]`;\n }\n if (iframe.id)\n return `#${iframe.id}`;\n return \"iframe\";\n}\nvar IframeHandler = class {\n constructor(document2) {\n this._observer = null;\n this._enabled = false;\n this._trackedIframes = /* @__PURE__ */ new WeakSet();\n this._document = document2;\n }\n setOnIframeLoad(cb) {\n this._onIframeLoad = cb;\n }\n enable() {\n if (this._enabled)\n return;\n this._enabled = true;\n if (!this._document.body)\n return;\n this._observer = new MutationObserver((mutations) => this._handleMutations(mutations));\n this._observer.observe(this._document.body, {\n childList: true,\n subtree: true\n });\n this._trackExistingIframes();\n console.log(\"[IframeHandler] Enabled \\u2014 watching for iframe load events\");\n }\n disable() {\n if (!this._enabled)\n return;\n this._enabled = false;\n if (this._observer) {\n this._observer.disconnect();\n this._observer = null;\n }\n log3(\"Disabled\");\n }\n _trackExistingIframes() {\n for (const iframe of this._document.querySelectorAll(\"iframe\"))\n this._trackIframe(iframe);\n }\n _handleMutations(mutations) {\n if (!this._enabled)\n return;\n for (const mutation of mutations) {\n if (mutation.type !== \"childList\")\n continue;\n for (const node of mutation.addedNodes) {\n if (node instanceof HTMLIFrameElement)\n this._trackIframe(node);\n if (node instanceof Element) {\n for (const iframe of node.querySelectorAll(\"iframe\"))\n this._trackIframe(iframe);\n }\n }\n }\n }\n _trackIframe(iframe) {\n var _a, _b;\n if (this._trackedIframes.has(iframe))\n return;\n this._trackedIframes.add(iframe);\n iframe.addEventListener(\"load\", () => {\n var _a2;\n if (!this._enabled)\n return;\n const selector = buildIframeSelector(iframe);\n console.log(\"[IframeHandler] Iframe loaded:\", selector);\n (_a2 = this._onIframeLoad) == null ? void 0 : _a2.call(this, { selector });\n }, { once: true });\n try {\n if (((_a = iframe.contentDocument) == null ? void 0 : _a.readyState) === \"complete\") {\n const selector = buildIframeSelector(iframe);\n console.log(\"[IframeHandler] Iframe already loaded:\", selector);\n (_b = this._onIframeLoad) == null ? void 0 : _b.call(this, { selector });\n }\n } catch {\n }\n }\n};\n\n// packages/injected/src/recorder/recorder.ts\nvar HighlightColors2 = {\n multiple: \"#f6b26b7f\",\n single: \"#6fa8dc7f\",\n assert: \"#8acae480\",\n action: \"#dc6f6f7f\",\n snapshot: \"#9c7fe480\"\n // Purple for visual snapshots\n};\nfunction computeScopedSelector(injectedScript, element, testIdAttributeName) {\n const elementInfo = injectedScript.generateSelector(element, { testIdAttributeName });\n let selector = elementInfo.selector;\n let scoped;\n const scopingResult = applyScopingHook(injectedScript, element, elementInfo.selector, elementInfo.elements);\n if (scopingResult) {\n selector = scopingResult.selector;\n if (!scopingResult.isFormContainer && !scopingResult.usesTextFilter) {\n scoped = {\n container: scopingResult.containerSelector,\n index: scopingResult.containerIndex,\n relative: scopingResult.relativeSelector\n };\n }\n }\n return { selector, scoped };\n}\nvar NoneTool = class {\n};\nvar InspectTool = class {\n constructor(recorder, assertVisibility) {\n this._hoveredModel = null;\n this._hoveredElement = null;\n this._recorder = recorder;\n this._assertVisibility = assertVisibility;\n }\n cursor() {\n return \"pointer\";\n }\n uninstall() {\n this._hoveredModel = null;\n this._hoveredElement = null;\n }\n onClick(event) {\n var _a;\n consumeEvent5(event);\n if (event.button !== 0)\n return;\n if ((_a = this._hoveredModel) == null ? void 0 : _a.selector)\n this._commit(this._hoveredModel.selector, this._hoveredModel);\n }\n onPointerDown(event) {\n consumeEvent5(event);\n }\n onPointerUp(event) {\n consumeEvent5(event);\n }\n onMouseDown(event) {\n consumeEvent5(event);\n }\n onMouseUp(event) {\n consumeEvent5(event);\n }\n onMouseMove(event) {\n var _a;\n consumeEvent5(event);\n let target = this._recorder.deepEventTarget(event);\n if (!target.isConnected)\n target = null;\n if (this._hoveredElement === target)\n return;\n this._hoveredElement = target;\n let model = null;\n if (this._hoveredElement) {\n const generated = this._recorder.injectedScript.generateSelector(this._hoveredElement, { testIdAttributeName: this._recorder.state.testIdAttributeName, multiple: false });\n const scopingResult = applyScopingHook(\n this._recorder.injectedScript,\n this._hoveredElement,\n generated.selector,\n generated.elements\n );\n const finalSelector = scopingResult ? scopingResult.selector : generated.selector;\n const finalElements = scopingResult ? scopingResult.elements : generated.elements;\n model = {\n selector: finalSelector,\n elements: finalElements,\n tooltipText: this._recorder.injectedScript.utils.asLocator(this._recorder.state.language, finalSelector),\n color: this._assertVisibility ? HighlightColors2.assert : HighlightColors2.single\n };\n }\n if (((_a = this._hoveredModel) == null ? void 0 : _a.selector) === (model == null ? void 0 : model.selector))\n return;\n this._hoveredModel = model;\n this._recorder.updateHighlight(model, true);\n }\n onMouseEnter(event) {\n consumeEvent5(event);\n }\n onMouseLeave(event) {\n consumeEvent5(event);\n const window2 = this._recorder.injectedScript.window;\n if (window2.top !== window2 && this._recorder.deepEventTarget(event).nodeType === Node.DOCUMENT_NODE)\n this._reset(true);\n }\n onKeyDown(event) {\n consumeEvent5(event);\n if (event.key === \"Escape\") {\n if (this._assertVisibility)\n this._recorder.setMode(\"recording\");\n }\n }\n onKeyUp(event) {\n consumeEvent5(event);\n }\n onScroll(event) {\n this._reset(false);\n }\n _commit(selector, model) {\n var _a;\n if (this._assertVisibility) {\n this._recorder.recordAction({\n name: \"assertVisible\",\n selector,\n signals: [],\n timestamp: getTimestamp10(this._recorder)\n });\n this._recorder.setMode(\"recording\");\n (_a = this._recorder.overlay) == null ? void 0 : _a.flashToolSucceeded(\"assertingVisibility\");\n } else {\n this._recorder.elementPicked(selector, model);\n }\n }\n _reset(userGesture) {\n this._hoveredElement = null;\n this._hoveredModel = null;\n this._recorder.updateHighlight(null, userGesture);\n }\n};\nvar RecordActionTool = class {\n constructor(recorder) {\n this._hoveredModel = null;\n this._hoveredElement = null;\n this._activeModel = null;\n this._expectProgrammaticKeyUp = false;\n this._observer = null;\n this._recorder = recorder;\n this._performingActions = /* @__PURE__ */ new Set();\n this._dialog = new Dialog(recorder);\n }\n cursor() {\n return \"pointer\";\n }\n _installObserverIfNeeded() {\n var _a;\n if (this._observer)\n return;\n if (!((_a = this._recorder.injectedScript.document) == null ? void 0 : _a.body))\n return;\n this._observer = new MutationObserver((mutations) => {\n if (!this._hoveredElement)\n return;\n for (const mutation of mutations) {\n for (const node of mutation.removedNodes) {\n if (node === this._hoveredElement || node.contains(this._hoveredElement))\n this._resetHoveredModel();\n }\n }\n });\n this._observer.observe(this._recorder.injectedScript.document.body, { childList: true, subtree: true });\n }\n uninstall() {\n var _a;\n (_a = this._observer) == null ? void 0 : _a.disconnect();\n this._observer = null;\n this._hoveredModel = null;\n this._hoveredElement = null;\n this._activeModel = null;\n this._expectProgrammaticKeyUp = false;\n this._dialog.close();\n }\n onClick(event) {\n var _a, _b, _c;\n this._lastClickX = event.clientX;\n this._lastClickY = event.clientY;\n if (this._dialog.isShowing()) {\n if (event.button === 2 && event.type === \"auxclick\") {\n consumeEvent5(event);\n }\n return;\n }\n if (isRangeInput(this._hoveredElement))\n return;\n if (this._shouldIgnoreMouseEvent(event))\n return;\n const target = this._recorder.deepEventTarget(event);\n const fileInput = this._findFileInput(target);\n if (fileInput) {\n if (!this._activeModel) {\n const selector = (_b = (_a = this._hoveredModel) == null ? void 0 : _a.selector) != null ? _b : this._recorder.injectedScript.generateSelector(fileInput, { testIdAttributeName: this._recorder.state.testIdAttributeName }).selector;\n this._activeModel = (_c = this._hoveredModel) != null ? _c : { selector, elements: [fileInput], color: \"#dc6f6f7f\" };\n }\n return;\n }\n if (this._actionInProgress(event))\n return;\n if (this._consumedDueToNoModel(event, this._hoveredModel))\n return;\n if (event.button === 2 && event.type === \"auxclick\") {\n this._showActionListDialog(this._hoveredModel, event);\n return;\n }\n const checkbox = asCheckbox(this._recorder.deepEventTarget(event));\n if (checkbox && event.detail === 1) {\n this._performAction({\n name: checkbox.checked ? \"check\" : \"uncheck\",\n selector: this._hoveredModel.selector,\n signals: [],\n timestamp: getTimestamp10(this._recorder)\n });\n return;\n }\n this._cancelPendingClickAction();\n let targetSelector = this._hoveredModel.selector;\n let shouldAutoDisableNestedTool = false;\n if (this._recorder.pointerEventsOverrideEnabled) {\n const clickedElement = this._recorder.deepEventTarget(event);\n const nestedResult = this._recorder._nestedElementHandler.handleNestedClick(\n clickedElement,\n this._hoveredModel,\n this._recorder.injectedScript,\n this._recorder.state.testIdAttributeName\n );\n if (nestedResult) {\n targetSelector = nestedResult.targetSelector;\n shouldAutoDisableNestedTool = nestedResult.shouldAutoDisable;\n }\n }\n let inSubFrame;\n try {\n inSubFrame = window !== window.top;\n } catch {\n inSubFrame = true;\n }\n if (event.detail === 1) {\n const clickAction = {\n name: \"click\",\n selector: targetSelector,\n position: positionForEvent(event),\n signals: [],\n button: buttonForEvent(event),\n modifiers: modifiersForEvent(event),\n clickCount: event.detail,\n timestamp: getTimestamp10(this._recorder)\n };\n this._pendingClickAction = {\n action: clickAction,\n autoDisableNestedTool: shouldAutoDisableNestedTool,\n timeout: inSubFrame ? 0 : this._recorder.injectedScript.utils.builtins.setTimeout(() => this._commitPendingClickAction(), 200)\n };\n if (inSubFrame)\n this._commitPendingClickAction();\n }\n }\n onDblClick(event) {\n if (this._dialog.isShowing())\n return;\n if (isRangeInput(this._hoveredElement))\n return;\n if (this._shouldIgnoreMouseEvent(event))\n return;\n if (this._actionInProgress(event))\n return;\n if (this._consumedDueToNoModel(event, this._hoveredModel))\n return;\n this._cancelPendingClickAction();\n this._performAction({\n name: \"click\",\n selector: this._hoveredModel.selector,\n position: positionForEvent(event),\n signals: [],\n button: buttonForEvent(event),\n modifiers: modifiersForEvent(event),\n clickCount: event.detail,\n timestamp: getTimestamp10(this._recorder)\n });\n if (this._recorder.pointerEventsOverrideEnabled) {\n this._recorder.togglePointerEventsOverride();\n }\n }\n _commitPendingClickAction() {\n if (this._pendingClickAction) {\n this._performAction(this._pendingClickAction.action);\n if (this._pendingClickAction.autoDisableNestedTool && this._recorder.pointerEventsOverrideEnabled) {\n this._recorder.togglePointerEventsOverride();\n }\n }\n this._cancelPendingClickAction();\n }\n _cancelPendingClickAction() {\n if (this._pendingClickAction)\n this._recorder.injectedScript.utils.builtins.clearTimeout(this._pendingClickAction.timeout);\n this._pendingClickAction = void 0;\n }\n onContextMenu(event) {\n if (this._dialog.isShowing()) {\n consumeEvent5(event);\n return;\n }\n if (this._shouldIgnoreMouseEvent(event))\n return;\n if (this._actionInProgress(event))\n return;\n if (this._consumedDueToNoModel(event, this._hoveredModel))\n return;\n this._showActionListDialog(this._hoveredModel, event);\n }\n onPointerDown(event) {\n if (this._dialog.isShowing())\n return;\n if (this._shouldIgnoreMouseEvent(event))\n return;\n this._consumeWhenAboutToPerform(event);\n }\n onPointerUp(event) {\n if (this._dialog.isShowing())\n return;\n if (this._shouldIgnoreMouseEvent(event))\n return;\n this._consumeWhenAboutToPerform(event);\n }\n onMouseDown(event) {\n if (this._dialog.isShowing())\n return;\n if (this._shouldIgnoreMouseEvent(event))\n return;\n this._consumeWhenAboutToPerform(event);\n this._activeModel = this._hoveredModel;\n }\n onMouseUp(event) {\n if (this._dialog.isShowing())\n return;\n if (this._shouldIgnoreMouseEvent(event))\n return;\n this._consumeWhenAboutToPerform(event);\n }\n onMouseMove(event) {\n if (this._dialog.isShowing())\n return;\n const target = this._recorder.deepEventTarget(event);\n if (this._hoveredElement === target)\n return;\n this._hoveredElement = target;\n this._updateModelForHoveredElement();\n }\n onMouseLeave(event) {\n if (this._dialog.isShowing())\n return;\n const window2 = this._recorder.injectedScript.window;\n if (window2.top !== window2 && this._recorder.deepEventTarget(event).nodeType === Node.DOCUMENT_NODE) {\n this._hoveredElement = null;\n this._updateModelForHoveredElement();\n }\n }\n onFocus(event) {\n if (this._dialog.isShowing())\n return;\n this._onFocus(event.isTrusted);\n }\n onInput(event) {\n var _a, _b, _c, _d;\n if (this._dialog.isShowing())\n return;\n const target = this._recorder.deepEventTarget(event);\n if (target.nodeName === \"INPUT\" && target.type.toLowerCase() === \"file\") {\n const selector = (_b = (_a = this._activeModel) == null ? void 0 : _a.selector) != null ? _b : this._recorder.injectedScript.generateSelector(target, { testIdAttributeName: this._recorder.state.testIdAttributeName }).selector;\n const fileList = [...target.files || []];\n const files = fileList.map((file) => file.name);\n const webkitdirectory = target.webkitdirectory === true;\n const relativePaths = fileList.map((file) => file.webkitRelativePath || \"\");\n const triggerEl = (_d = (_c = this._activeModel) == null ? void 0 : _c.elements) == null ? void 0 : _d[0];\n const isDirectInputClick = !triggerEl || triggerEl === target;\n if (!isDirectInputClick) {\n this._recordAction({\n name: \"fileChooser\",\n selector,\n signals: [],\n files,\n webkitdirectory,\n relativePaths,\n timestamp: getTimestamp10(this._recorder)\n });\n return;\n }\n this._recordAction({\n name: \"setInputFiles\",\n selector,\n signals: [],\n files,\n webkitdirectory,\n relativePaths,\n timestamp: getTimestamp10(this._recorder)\n });\n return;\n }\n if (isRangeInput(target)) {\n this._recordAction({\n name: \"fill\",\n // must use hoveredModel instead of activeModel for it to work in webkit\n selector: this._hoveredModel.selector,\n signals: [],\n text: target.value,\n timestamp: getTimestamp10(this._recorder)\n });\n return;\n }\n if ([\"INPUT\", \"TEXTAREA\"].includes(target.nodeName) || target.isContentEditable) {\n if (target.nodeName === \"INPUT\" && [\"checkbox\", \"radio\"].includes(target.type.toLowerCase())) {\n return;\n }\n if (this._consumedDueWrongTarget(event))\n return;\n this._recordAction({\n name: \"fill\",\n selector: this._activeModel.selector,\n signals: [],\n text: target.isContentEditable ? target.innerText : target.value,\n timestamp: getTimestamp10(this._recorder)\n });\n }\n if (target.nodeName === \"SELECT\") {\n const selectElement = target;\n this._recordAction({\n name: \"select\",\n selector: this._activeModel.selector,\n options: [...selectElement.selectedOptions].map((option) => option.value),\n signals: [],\n timestamp: getTimestamp10(this._recorder)\n });\n }\n }\n onKeyDown(event) {\n if (this._dialog.isShowing())\n return;\n if (!this._shouldGenerateKeyPressFor(event))\n return;\n if (this._actionInProgress(event)) {\n this._expectProgrammaticKeyUp = true;\n return;\n }\n if (this._consumedDueWrongTarget(event))\n return;\n if (event.key === \" \") {\n const checkbox = asCheckbox(this._recorder.deepEventTarget(event));\n if (checkbox && event.detail === 0) {\n this._performAction({\n name: checkbox.checked ? \"uncheck\" : \"check\",\n selector: this._activeModel.selector,\n signals: [],\n timestamp: getTimestamp10(this._recorder)\n });\n return;\n }\n }\n this._performAction({\n name: \"press\",\n selector: this._activeModel.selector,\n signals: [],\n key: event.key,\n modifiers: modifiersForEvent(event),\n timestamp: getTimestamp10(this._recorder)\n });\n }\n onKeyUp(event) {\n if (this._dialog.isShowing())\n return;\n if (!this._shouldGenerateKeyPressFor(event))\n return;\n if (!this._expectProgrammaticKeyUp) {\n consumeEvent5(event);\n return;\n }\n this._expectProgrammaticKeyUp = false;\n }\n onScroll(event) {\n if (this._dialog.isShowing())\n return;\n this._resetHoveredModel();\n }\n _showActionListDialog(model, event) {\n consumeEvent5(event);\n const actionPosition = positionForEvent(event);\n const actions = [\n {\n title: \"Click\",\n cb: () => this._performAction({\n name: \"click\",\n selector: model.selector,\n position: actionPosition,\n signals: [],\n button: \"left\",\n modifiers: 0,\n clickCount: 1,\n timestamp: getTimestamp10(this._recorder)\n })\n },\n {\n title: \"Right click\",\n cb: () => this._performAction({\n name: \"click\",\n selector: model.selector,\n position: actionPosition,\n signals: [],\n button: \"right\",\n modifiers: 0,\n clickCount: 1,\n timestamp: getTimestamp10(this._recorder)\n })\n },\n {\n title: \"Double click\",\n cb: () => this._performAction({\n name: \"click\",\n selector: model.selector,\n position: actionPosition,\n signals: [],\n button: \"left\",\n modifiers: 0,\n clickCount: 2,\n timestamp: getTimestamp10(this._recorder)\n })\n },\n {\n title: \"Hover\",\n cb: () => this._performAction({\n name: \"hover\",\n selector: model.selector,\n position: actionPosition,\n signals: [],\n timestamp: getTimestamp10(this._recorder)\n })\n },\n {\n title: \"Pick locator\",\n cb: () => this._recorder.elementPicked(model.selector, model)\n }\n ];\n const listElement = this._recorder.document.createElement(\"x-pw-action-list\");\n listElement.setAttribute(\"role\", \"list\");\n listElement.setAttribute(\"aria-label\", \"Choose action\");\n for (const action of actions) {\n const actionElement = this._recorder.document.createElement(\"x-pw-action-item\");\n actionElement.setAttribute(\"role\", \"listitem\");\n actionElement.textContent = action.title;\n actionElement.setAttribute(\"aria-label\", action.title);\n actionElement.addEventListener(\"click\", () => {\n this._dialog.close();\n action.cb();\n });\n listElement.appendChild(actionElement);\n }\n const dialogElement = this._dialog.show({\n label: \"Choose action\",\n body: listElement,\n autosize: true\n });\n const anchorBox = this._recorder.highlight.firstTooltipBox() || model.elements[0].getBoundingClientRect();\n const dialogPosition = this._recorder.highlight.tooltipPosition(anchorBox, dialogElement);\n this._dialog.moveTo(dialogPosition.anchorTop, dialogPosition.anchorLeft);\n }\n _resetHoveredModel() {\n this._hoveredModel = null;\n this._hoveredElement = null;\n this._updateHighlight(false);\n }\n _onFocus(userGesture) {\n const activeElement = deepActiveElement(this._recorder.document);\n if (activeElement === this._recorder.document.body)\n return;\n const result = activeElement ? this._recorder.injectedScript.generateSelector(activeElement, { testIdAttributeName: this._recorder.state.testIdAttributeName }) : null;\n let finalSelector = result == null ? void 0 : result.selector;\n let finalElements = result == null ? void 0 : result.elements;\n if (activeElement && result) {\n const scopingResult = applyScopingHook(\n this._recorder.injectedScript,\n activeElement,\n result.selector,\n result.elements\n );\n if (scopingResult) {\n finalSelector = scopingResult.selector;\n finalElements = scopingResult.elements;\n }\n }\n this._activeModel = result && finalSelector ? { ...result, selector: finalSelector, elements: finalElements, color: HighlightColors2.action } : null;\n if (userGesture) {\n this._hoveredElement = activeElement;\n this._updateModelForHoveredElement();\n }\n }\n _shouldIgnoreMouseEvent(event) {\n const target = this._recorder.deepEventTarget(event);\n const nodeName = target.nodeName;\n if (nodeName === \"SELECT\" || nodeName === \"OPTION\")\n return true;\n if (nodeName === \"INPUT\" && [\"date\", \"range\"].includes(target.type))\n return true;\n return false;\n }\n _actionInProgress(event) {\n const isKeyEvent = event instanceof KeyboardEvent;\n const isMouseOrPointerEvent = event instanceof MouseEvent || event instanceof PointerEvent;\n for (const action of this._performingActions) {\n if (isKeyEvent && action.name === \"press\" && event.key === action.key)\n return true;\n if (isMouseOrPointerEvent && (action.name === \"click\" || action.name === \"hover\" || action.name === \"check\" || action.name === \"uncheck\"))\n return true;\n }\n consumeEvent5(event);\n return false;\n }\n _consumedDueToNoModel(event, model) {\n if (model)\n return false;\n consumeEvent5(event);\n return true;\n }\n _consumedDueWrongTarget(event) {\n if (this._activeModel && this._activeModel.elements[0] === this._recorder.deepEventTarget(event))\n return false;\n consumeEvent5(event);\n return true;\n }\n // Returns the file input that a click on `element` would activate, or\n // null if none. Only considers the element itself, a <label for=…>\n // pointing at a file input, or a file input within its descendants —\n // never ancestors, to avoid matching siblings of a file-input wrapper.\n _findFileInput(element) {\n if (element.nodeName === \"INPUT\" && element.type.toLowerCase() === \"file\")\n return element;\n if (element.nodeName === \"LABEL\") {\n const forId = element.htmlFor;\n if (forId) {\n const target = element.ownerDocument.getElementById(forId);\n if (target && target.nodeName === \"INPUT\" && target.type.toLowerCase() === \"file\")\n return target;\n }\n }\n const descendant = element.querySelector('input[type=\"file\"]');\n if (descendant)\n return descendant;\n return null;\n }\n _consumeWhenAboutToPerform(event) {\n if (!this._performingActions.size)\n consumeEvent5(event);\n }\n _recordAction(action) {\n this._recorder.recordAction(action);\n }\n _performAction(action) {\n this._recorder.updateHighlight(null, false);\n this._performingActions.add(action);\n const promise = this._recorder.performAction(action).then(() => {\n this._performingActions.delete(action);\n this._onFocus(false);\n });\n if (!this._recorder.injectedScript.isUnderTest)\n return;\n void promise.then(() => {\n console.error(\"Action performed for test: \" + JSON.stringify({\n // eslint-disable-line no-console\n hovered: this._hoveredModel ? this._hoveredModel.selector : null,\n active: this._activeModel ? this._activeModel.selector : null\n }));\n });\n }\n _shouldGenerateKeyPressFor(event) {\n if (typeof event.key !== \"string\")\n return false;\n if (event.key === \"Enter\" && (this._recorder.deepEventTarget(event).nodeName === \"TEXTAREA\" || this._recorder.deepEventTarget(event).isContentEditable))\n return false;\n if ([\"Backspace\", \"Delete\", \"AltGraph\"].includes(event.key))\n return false;\n if (event.key === \"@\" && event.code === \"KeyL\")\n return false;\n if (navigator.platform.includes(\"Mac\")) {\n if (event.key === \"v\" && event.metaKey)\n return false;\n } else {\n if (event.key === \"v\" && event.ctrlKey)\n return false;\n if (event.key === \"Insert\" && event.shiftKey)\n return false;\n }\n if ([\"Shift\", \"Control\", \"Meta\", \"Alt\", \"Process\"].includes(event.key))\n return false;\n const hasModifier = event.ctrlKey || event.altKey || event.metaKey;\n if (event.key.length === 1 && !hasModifier)\n return !!asCheckbox(this._recorder.deepEventTarget(event));\n return true;\n }\n _updateModelForHoveredElement() {\n this._installObserverIfNeeded();\n if (this._performingActions.size)\n return;\n if (!this._hoveredElement || !this._hoveredElement.isConnected) {\n this._hoveredModel = null;\n this._hoveredElement = null;\n this._updateHighlight(true);\n return;\n }\n let { selector, elements } = this._recorder.injectedScript.generateSelector(this._hoveredElement, {\n testIdAttributeName: this._recorder.state.testIdAttributeName\n });\n const scopingResult = applyScopingHook(\n this._recorder.injectedScript,\n this._hoveredElement,\n selector,\n elements\n );\n if (scopingResult) {\n selector = scopingResult.selector;\n elements = scopingResult.elements;\n }\n if (this._hoveredModel && this._hoveredModel.selector === selector)\n return;\n this._hoveredModel = selector ? { selector, elements, color: HighlightColors2.action } : null;\n this._updateHighlight(true);\n }\n _updateHighlight(userGesture) {\n this._recorder.updateHighlight(this._hoveredModel, userGesture);\n }\n};\nvar JsonRecordActionTool = class {\n constructor(recorder) {\n this._recorder = recorder;\n }\n install() {\n this._recorder.clearHighlight();\n }\n uninstall() {\n }\n onClick(event) {\n const element = this._recorder.deepEventTarget(event);\n if (isRangeInput(element))\n return;\n if (event.button === 2 && event.type === \"auxclick\")\n return;\n if (this._shouldIgnoreMouseEvent(event))\n return;\n const checkbox = asCheckbox(element);\n const { ariaSnapshot, selector, ref, scoped } = this._ariaSnapshot(element);\n if (checkbox && event.detail === 1) {\n this._recorder.recordAction({\n name: checkbox.checked ? \"check\" : \"uncheck\",\n selector,\n ref,\n scoped,\n signals: [],\n ariaSnapshot,\n timestamp: getTimestamp10(this._recorder)\n });\n return;\n }\n this._recorder.recordAction({\n name: \"click\",\n selector,\n ref,\n scoped,\n ariaSnapshot,\n position: positionForEvent(event),\n signals: [],\n button: buttonForEvent(event),\n modifiers: modifiersForEvent(event),\n clickCount: event.detail,\n timestamp: getTimestamp10(this._recorder)\n });\n }\n onContextMenu(event) {\n const element = this._recorder.deepEventTarget(event);\n const { ariaSnapshot, selector, ref, scoped } = this._ariaSnapshot(element);\n this._recorder.recordAction({\n name: \"click\",\n selector,\n ref,\n scoped,\n ariaSnapshot,\n position: positionForEvent(event),\n signals: [],\n button: \"right\",\n modifiers: modifiersForEvent(event),\n clickCount: 1,\n timestamp: getTimestamp10(this._recorder)\n });\n }\n onInput(event) {\n const element = this._recorder.deepEventTarget(event);\n const { ariaSnapshot, selector, ref, scoped } = this._ariaSnapshot(element);\n if (isRangeInput(element)) {\n this._recorder.recordAction({\n name: \"fill\",\n selector,\n ref,\n scoped,\n ariaSnapshot,\n signals: [],\n text: element.value,\n timestamp: getTimestamp10(this._recorder)\n });\n return;\n }\n if ([\"INPUT\", \"TEXTAREA\"].includes(element.nodeName) || element.isContentEditable) {\n if (element.nodeName === \"INPUT\" && [\"checkbox\", \"radio\"].includes(element.type.toLowerCase())) {\n return;\n }\n this._recorder.recordAction({\n name: \"fill\",\n ref,\n selector,\n scoped,\n ariaSnapshot,\n signals: [],\n text: element.isContentEditable ? element.innerText : element.value,\n timestamp: getTimestamp10(this._recorder)\n });\n return;\n }\n if (element.nodeName === \"SELECT\") {\n const selectElement = element;\n this._recorder.recordAction({\n name: \"select\",\n selector,\n ref,\n scoped,\n ariaSnapshot,\n options: [...selectElement.selectedOptions].map((option) => option.value),\n signals: [],\n timestamp: getTimestamp10(this._recorder)\n });\n return;\n }\n }\n onKeyDown(event) {\n if (!this._shouldGenerateKeyPressFor(event))\n return;\n const element = this._recorder.deepEventTarget(event);\n const { ariaSnapshot, selector, ref, scoped } = this._ariaSnapshot(element);\n if (event.key === \" \") {\n const checkbox = asCheckbox(element);\n if (checkbox && event.detail === 0) {\n this._recorder.recordAction({\n name: checkbox.checked ? \"uncheck\" : \"check\",\n selector,\n ref,\n scoped,\n ariaSnapshot,\n signals: [],\n timestamp: getTimestamp10(this._recorder)\n });\n return;\n }\n }\n this._recorder.recordAction({\n name: \"press\",\n selector,\n ref,\n scoped,\n ariaSnapshot,\n signals: [],\n key: event.key,\n modifiers: modifiersForEvent(event),\n timestamp: getTimestamp10(this._recorder)\n });\n }\n _shouldIgnoreMouseEvent(event) {\n const target = this._recorder.deepEventTarget(event);\n const nodeName = target.nodeName;\n if (nodeName === \"SELECT\" || nodeName === \"OPTION\")\n return true;\n if (nodeName === \"INPUT\" && [\"date\", \"range\"].includes(target.type))\n return true;\n return false;\n }\n _shouldGenerateKeyPressFor(event) {\n if (typeof event.key !== \"string\")\n return false;\n if (event.key === \"Enter\" && (this._recorder.deepEventTarget(event).nodeName === \"TEXTAREA\" || this._recorder.deepEventTarget(event).isContentEditable))\n return false;\n if ([\"Backspace\", \"Delete\", \"AltGraph\"].includes(event.key))\n return false;\n if (event.key === \"@\" && event.code === \"KeyL\")\n return false;\n if (navigator.platform.includes(\"Mac\")) {\n if (event.key === \"v\" && event.metaKey)\n return false;\n } else {\n if (event.key === \"v\" && event.ctrlKey)\n return false;\n if (event.key === \"Insert\" && event.shiftKey)\n return false;\n }\n if ([\"Shift\", \"Control\", \"Meta\", \"Alt\", \"Process\"].includes(event.key))\n return false;\n const hasModifier = event.ctrlKey || event.altKey || event.metaKey;\n if (event.key.length === 1 && !hasModifier)\n return !this._isEditable(this._recorder.deepEventTarget(event));\n return true;\n }\n _isEditable(element) {\n if (element.nodeName === \"TEXTAREA\" || element.nodeName === \"INPUT\")\n return true;\n if (element.isContentEditable)\n return true;\n return false;\n }\n _ariaSnapshot(element) {\n const { ariaSnapshot, refs } = this._recorder.injectedScript.ariaSnapshotForRecorder();\n const ref = element ? refs.get(element) : void 0;\n let finalSelector;\n let scoped;\n if (element) {\n const computed = computeScopedSelector(this._recorder.injectedScript, element, this._recorder.state.testIdAttributeName);\n finalSelector = computed.selector;\n scoped = computed.scoped;\n }\n return { ariaSnapshot, selector: finalSelector, ref, scoped };\n }\n};\nvar TextAssertionTool = class {\n constructor(recorder, kind) {\n this._hoverHighlight = null;\n this._action = null;\n this._recorder = recorder;\n this._textCache = /* @__PURE__ */ new Map();\n this._kind = kind;\n this._dialog = new Dialog(recorder);\n }\n cursor() {\n return \"pointer\";\n }\n uninstall() {\n this._dialog.close();\n this._hoverHighlight = null;\n }\n onClick(event) {\n consumeEvent5(event);\n if (this._kind === \"value\") {\n this._commitAssertValue();\n } else {\n if (!this._dialog.isShowing())\n this._showDialog();\n }\n }\n onMouseDown(event) {\n const target = this._recorder.deepEventTarget(event);\n if (this._elementHasValue(target))\n event.preventDefault();\n else\n consumeEvent5(event);\n }\n onPointerDown(event) {\n consumeEvent5(event);\n }\n onPointerUp(event) {\n var _a;\n const target = (_a = this._hoverHighlight) == null ? void 0 : _a.elements[0];\n if (this._kind === \"value\" && target && (target.nodeName === \"INPUT\" || target.nodeName === \"SELECT\") && target.disabled) {\n this._commitAssertValue();\n }\n }\n onMouseMove(event) {\n var _a;\n if (this._dialog.isShowing())\n return;\n const target = this._recorder.deepEventTarget(event);\n if (((_a = this._hoverHighlight) == null ? void 0 : _a.elements[0]) === target)\n return;\n if (this._kind === \"text\" || this._kind === \"snapshot\") {\n this._hoverHighlight = this._recorder.injectedScript.utils.elementText(this._textCache, target).full ? { elements: [target], selector: \"\", color: HighlightColors2.assert } : null;\n } else if (this._elementHasValue(target)) {\n const generated = this._recorder.injectedScript.generateSelector(target, { testIdAttributeName: this._recorder.state.testIdAttributeName });\n this._hoverHighlight = { selector: generated.selector, elements: generated.elements, color: HighlightColors2.assert };\n } else {\n this._hoverHighlight = null;\n }\n this._recorder.updateHighlight(this._hoverHighlight, true);\n }\n onKeyDown(event) {\n if (event.key === \"Escape\")\n this._recorder.setMode(\"recording\");\n consumeEvent5(event);\n }\n onScroll(event) {\n this._recorder.updateHighlight(this._hoverHighlight, false);\n }\n _elementHasValue(element) {\n return element.nodeName === \"TEXTAREA\" || element.nodeName === \"SELECT\" || element.nodeName === \"INPUT\" && ![\"button\", \"image\", \"reset\", \"submit\"].includes(element.type);\n }\n _generateAction() {\n var _a;\n this._textCache.clear();\n const target = (_a = this._hoverHighlight) == null ? void 0 : _a.elements[0];\n if (!target)\n return null;\n if (this._kind === \"value\") {\n if (!this._elementHasValue(target))\n return null;\n const { selector } = this._recorder.injectedScript.generateSelector(target, { testIdAttributeName: this._recorder.state.testIdAttributeName });\n if (target.nodeName === \"INPUT\" && [\"checkbox\", \"radio\"].includes(target.type.toLowerCase())) {\n return {\n name: \"assertChecked\",\n selector,\n signals: [],\n // Interestingly, inputElement.checked is reversed inside this event handler.\n checked: !target.checked,\n timestamp: getTimestamp10(this._recorder)\n };\n } else {\n return {\n name: \"assertValue\",\n selector,\n signals: [],\n value: target.value,\n timestamp: getTimestamp10(this._recorder)\n };\n }\n } else if (this._kind === \"snapshot\") {\n const generated = this._recorder.injectedScript.generateSelector(target, { testIdAttributeName: this._recorder.state.testIdAttributeName, forTextExpect: true });\n this._hoverHighlight = { selector: generated.selector, elements: generated.elements, color: HighlightColors2.assert };\n this._recorder.updateHighlight(this._hoverHighlight, true);\n return {\n name: \"assertSnapshot\",\n selector: this._hoverHighlight.selector,\n signals: [],\n ariaSnapshot: this._recorder.injectedScript.ariaSnapshot(target, { mode: \"codegen\" }),\n timestamp: getTimestamp10(this._recorder)\n };\n } else {\n const closestTd = target.closest(\"td\");\n const isInTableCell = closestTd && closestTd.closest(\"tr\");\n let generated = this._recorder.injectedScript.generateSelector(target, {\n testIdAttributeName: this._recorder.state.testIdAttributeName,\n forTextExpect: !isInTableCell\n });\n const scopingResult = applyScopingHook(\n this._recorder.injectedScript,\n target,\n generated.selector,\n generated.elements\n );\n if (scopingResult) {\n generated = { selector: scopingResult.selector, selectors: [scopingResult.selector], elements: scopingResult.elements };\n }\n this._hoverHighlight = { selector: generated.selector, elements: generated.elements, color: HighlightColors2.assert };\n this._recorder.updateHighlight(this._hoverHighlight, true);\n return {\n name: \"assertText\",\n selector: this._hoverHighlight.selector,\n signals: [],\n text: this._recorder.injectedScript.utils.elementText(this._textCache, target).normalized,\n substring: true,\n timestamp: getTimestamp10(this._recorder)\n };\n }\n }\n _renderValue(action) {\n if ((action == null ? void 0 : action.name) === \"assertText\")\n return this._recorder.injectedScript.utils.normalizeWhiteSpace(action.text);\n if ((action == null ? void 0 : action.name) === \"assertChecked\")\n return String(action.checked);\n if ((action == null ? void 0 : action.name) === \"assertValue\")\n return action.value;\n if ((action == null ? void 0 : action.name) === \"assertSnapshot\")\n return action.ariaSnapshot;\n return \"\";\n }\n _commit() {\n if (!this._action || !this._dialog.isShowing())\n return;\n this._dialog.close();\n this._recorder.recordAction(this._action);\n this._recorder.setMode(\"recording\");\n showModalAfterAssertion();\n }\n _showDialog() {\n var _a, _b, _c, _d;\n if (!((_a = this._hoverHighlight) == null ? void 0 : _a.elements[0]))\n return;\n hideModalForAssertion();\n this._action = this._generateAction();\n if (((_b = this._action) == null ? void 0 : _b.name) === \"assertText\") {\n this._showTextDialog(this._action);\n } else if (((_c = this._action) == null ? void 0 : _c.name) === \"assertSnapshot\") {\n this._recorder.recordAction(this._action);\n this._recorder.setMode(\"recording\");\n (_d = this._recorder.overlay) == null ? void 0 : _d.flashToolSucceeded(\"assertingSnapshot\");\n }\n }\n _showTextDialog(action) {\n const textElement = this._recorder.document.createElement(\"textarea\");\n textElement.setAttribute(\"spellcheck\", \"false\");\n textElement.value = this._renderValue(action);\n textElement.classList.add(\"text-editor\");\n const updateAndValidate = () => {\n var _a;\n const newValue = this._recorder.injectedScript.utils.normalizeWhiteSpace(textElement.value);\n const target = (_a = this._hoverHighlight) == null ? void 0 : _a.elements[0];\n if (!target)\n return;\n action.text = newValue;\n const targetText = this._recorder.injectedScript.utils.elementText(this._textCache, target).normalized;\n const matches = newValue && targetText.includes(newValue);\n textElement.classList.toggle(\"does-not-match\", !matches);\n };\n textElement.addEventListener(\"input\", updateAndValidate);\n const label = \"Assert that element contains text\";\n const dialogElement = this._dialog.show({\n label,\n body: textElement,\n onCommit: () => this._commit()\n });\n const position = this._recorder.highlight.tooltipPosition(this._recorder.highlight.firstBox(), dialogElement);\n this._dialog.moveTo(position.anchorTop, position.anchorLeft);\n textElement.focus();\n }\n _commitAssertValue() {\n var _a;\n if (this._kind !== \"value\")\n return;\n const action = this._generateAction();\n if (!action)\n return;\n this._recorder.recordAction(action);\n this._recorder.setMode(\"recording\");\n (_a = this._recorder.overlay) == null ? void 0 : _a.flashToolSucceeded(\"assertingValue\");\n }\n};\nvar Overlay = class {\n // Track when snapshot toggle was activated\n //private _modularityToggled = false;\n constructor(recorder) {\n this._listeners = [];\n this._offsetX = 0;\n this._measure = { width: 0, height: 0 };\n this._snapshotToggleTime = null;\n this._recorder = recorder;\n const document2 = this._recorder.document;\n this._overlayElement = document2.createElement(\"x-pw-overlay\");\n const toolsListElement = document2.createElement(\"x-pw-tools-list\");\n this._overlayElement.appendChild(toolsListElement);\n this._dragHandle = document2.createElement(\"x-pw-tool-gripper\");\n this._dragHandle.appendChild(document2.createElement(\"x-div\"));\n toolsListElement.appendChild(this._dragHandle);\n this._recordToggle = this._recorder.document.createElement(\"x-pw-tool-item\");\n this._recordToggle.title = \"Record\";\n this._recordToggle.classList.add(\"record\");\n this._recordToggle.appendChild(this._recorder.document.createElement(\"x-div\"));\n toolsListElement.appendChild(this._recordToggle);\n this._pickLocatorToggle = this._recorder.document.createElement(\"x-pw-tool-item\");\n this._pickLocatorToggle.title = \"Pick locator\";\n this._pickLocatorToggle.classList.add(\"pick-locator\");\n this._pickLocatorToggle.appendChild(this._recorder.document.createElement(\"x-div\"));\n toolsListElement.appendChild(this._pickLocatorToggle);\n this._modularityToggle = this._recorder.document.createElement(\"x-pw-tool-item\");\n this._modularityToggle.title = \"Mark block\";\n this._modularityToggle.classList.add(\"modular\");\n this._modularityToggle.appendChild(this._recorder.document.createElement(\"x-div\"));\n toolsListElement.appendChild(this._modularityToggle);\n this._assertApiPayloadToggle = this._recorder.document.createElement(\"x-pw-tool-item\");\n this._assertApiPayloadToggle.title = \"Assert API Request\";\n this._assertApiPayloadToggle.classList.add(\"assert-api-payload\");\n this._assertApiPayloadToggle.appendChild(this._recorder.document.createElement(\"x-div\"));\n toolsListElement.appendChild(this._assertApiPayloadToggle);\n this._fileUploadToggle = this._recorder.document.createElement(\"x-pw-tool-item\");\n this._fileUploadToggle.title = \"Upload file\";\n this._fileUploadToggle.classList.add(\"file-upload\");\n this._fileUploadToggle.appendChild(this._recorder.document.createElement(\"x-div\"));\n toolsListElement.appendChild(this._fileUploadToggle);\n this._dragRecordToggle = this._recorder.document.createElement(\"x-pw-tool-item\");\n this._dragRecordToggle.title = \"Drag and drop\";\n this._dragRecordToggle.classList.add(\"drag-record\");\n this._dragRecordToggle.appendChild(this._recorder.document.createElement(\"x-div\"));\n toolsListElement.appendChild(this._dragRecordToggle);\n this._areaSelectToggle = this._recorder.document.createElement(\"x-pw-tool-item\");\n this._areaSelectToggle.title = \"Select area\";\n this._areaSelectToggle.classList.add(\"area-select\");\n this._areaSelectToggle.appendChild(this._recorder.document.createElement(\"x-div\"));\n toolsListElement.appendChild(this._areaSelectToggle);\n this._sketchToolToggle = this._recorder.document.createElement(\"x-pw-tool-item\");\n this._sketchToolToggle.title = \"Sketch Tool\";\n this._sketchToolToggle.classList.add(\"sketch-tool\");\n this._sketchToolToggle.appendChild(this._recorder.document.createElement(\"x-div\"));\n toolsListElement.appendChild(this._sketchToolToggle);\n this._gojsLinkToggle = this._recorder.document.createElement(\"x-pw-tool-item\");\n this._gojsLinkToggle.title = \"GoJS Link (click source node, then target node)\";\n this._gojsLinkToggle.classList.add(\"gojs-link\");\n this._gojsLinkToggle.appendChild(this._recorder.document.createElement(\"x-div\"));\n toolsListElement.appendChild(this._gojsLinkToggle);\n this._pointerEventsToggle = this._recorder.document.createElement(\"x-pw-tool-item\");\n this._pointerEventsToggle.title = \"Nested element selection\";\n this._pointerEventsToggle.classList.add(\"pointer-events\");\n this._pointerEventsToggle.appendChild(this._recorder.document.createElement(\"x-div\"));\n toolsListElement.appendChild(this._pointerEventsToggle);\n this._assertVisibilityToggle = this._recorder.document.createElement(\"x-pw-tool-item\");\n this._assertVisibilityToggle.title = \"Assert visibility\";\n this._assertVisibilityToggle.classList.add(\"visibility\");\n this._assertVisibilityToggle.appendChild(this._recorder.document.createElement(\"x-div\"));\n toolsListElement.appendChild(this._assertVisibilityToggle);\n this._assertTextToggle = this._recorder.document.createElement(\"x-pw-tool-item\");\n this._assertTextToggle.title = \"Assert text\";\n this._assertTextToggle.classList.add(\"text\");\n this._assertTextToggle.appendChild(this._recorder.document.createElement(\"x-div\"));\n toolsListElement.appendChild(this._assertTextToggle);\n this._assertValuesToggle = this._recorder.document.createElement(\"x-pw-tool-item\");\n this._assertValuesToggle.title = \"Assert value\";\n this._assertValuesToggle.classList.add(\"value\");\n this._assertValuesToggle.appendChild(this._recorder.document.createElement(\"x-div\"));\n toolsListElement.appendChild(this._assertValuesToggle);\n this._tableSnapshotToggle = this._recorder.document.createElement(\"x-pw-tool-item\");\n this._tableSnapshotToggle.title = \"Assert table cell\";\n this._tableSnapshotToggle.classList.add(\"table\");\n this._tableSnapshotToggle.appendChild(this._recorder.document.createElement(\"x-div\"));\n toolsListElement.appendChild(this._tableSnapshotToggle);\n this._assertVSnapshotToggle = this._recorder.document.createElement(\"x-pw-tool-item\");\n this._assertVSnapshotToggle.title = \"Snapshot: Double-toggle for page, Click for element, Drag for region\";\n this._assertVSnapshotToggle.classList.add(\"visual-snapshot\");\n this._assertVSnapshotToggle.appendChild(this._recorder.document.createElement(\"x-div\"));\n toolsListElement.appendChild(this._assertVSnapshotToggle);\n this._assertSnapshotToggle = this._recorder.document.createElement(\"x-pw-tool-item\");\n this._jsonMarkerButton = this._recorder.document.createElement(\"x-pw-tool-item\");\n this._updateVisualPosition();\n this._refreshListeners();\n }\n _refreshListeners() {\n removeEventListeners3(this._listeners);\n this._listeners = [\n addEventListener5(this._dragHandle, \"mousedown\", (event) => {\n this._dragState = { offsetX: this._offsetX, dragStart: { x: event.clientX, y: 0 } };\n }),\n addEventListener5(this._recordToggle, \"click\", () => {\n if (this._recordToggle.classList.contains(\"disabled\"))\n return;\n this._recorder.setMode(this._recorder.state.mode === \"none\" || this._recorder.state.mode === \"standby\" || this._recorder.state.mode === \"inspecting\" ? \"recording\" : \"standby\");\n }),\n addEventListener5(this._pickLocatorToggle, \"click\", () => {\n if (this._pickLocatorToggle.classList.contains(\"disabled\"))\n return;\n const newMode = {\n \"inspecting\": \"standby\",\n \"none\": \"inspecting\",\n \"standby\": \"inspecting\",\n \"recording\": \"recording-inspecting\",\n \"recording-inspecting\": \"recording\",\n \"assertingText\": \"recording-inspecting\",\n \"assertingVisibility\": \"recording-inspecting\",\n \"assertingValue\": \"recording-inspecting\",\n \"assertingSnapshot\": \"recording-inspecting\",\n \"assertingVSnapshot\": \"recording-inspecting\",\n \"assertingTableCell\": \"recording-inspecting\",\n \"recordingDrag\": \"recording\",\n \"recordingGoJSLink\": \"recording\",\n \"recordingArea\": \"recording\",\n \"recordingFileUpload\": \"recording\",\n \"recordingTableSnapshot\": \"recording\",\n \"recordingDomSnapshot\": \"recording\",\n \"recordingSketchTool\": \"recording\"\n };\n this._recorder.setMode(newMode[this._recorder.state.mode]);\n }),\n addEventListener5(this._assertVisibilityToggle, \"click\", () => {\n if (!this._assertVisibilityToggle.classList.contains(\"disabled\"))\n this._recorder.setMode(this._recorder.state.mode === \"assertingVisibility\" ? \"recording\" : \"assertingVisibility\");\n }),\n addEventListener5(this._assertTextToggle, \"click\", () => {\n if (!this._assertTextToggle.classList.contains(\"disabled\"))\n this._recorder.setMode(this._recorder.state.mode === \"assertingText\" ? \"recording\" : \"assertingText\");\n }),\n addEventListener5(this._assertValuesToggle, \"click\", () => {\n if (!this._assertValuesToggle.classList.contains(\"disabled\"))\n this._recorder.setMode(this._recorder.state.mode === \"assertingValue\" ? \"recording\" : \"assertingValue\");\n }),\n addEventListener5(this._assertSnapshotToggle, \"click\", () => {\n if (!this._assertSnapshotToggle.classList.contains(\"disabled\"))\n this._recorder.setMode(this._recorder.state.mode === \"assertingSnapshot\" ? \"recording\" : \"assertingSnapshot\");\n }),\n addEventListener5(this._assertVSnapshotToggle, \"click\", () => {\n if (this._assertVSnapshotToggle.classList.contains(\"disabled\"))\n return;\n const currentMode = this._recorder.state.mode;\n const isTogglingOff = currentMode === \"assertingVSnapshot\";\n if (isTogglingOff) {\n const now = Date.now();\n if (this._snapshotToggleTime && now - this._snapshotToggleTime < 1500) {\n VisualSnapshotTool.getNextCounter(this._recorder, \"page\").then((counter) => {\n const action = {\n name: \"visualSnapshot\",\n snapshotType: \"page\",\n filename: `page-${String(counter).padStart(3, \"0\")}.png`,\n fullPage: true,\n signals: [],\n timestamp: getTimestamp10(this._recorder)\n };\n this._recorder.recordAction(action);\n this.flashToolSucceeded(\"assertingVSnapshot\");\n });\n }\n this._snapshotToggleTime = null;\n this._recorder.setMode(\"recording\");\n } else {\n this._snapshotToggleTime = Date.now();\n this._recorder.setMode(\"assertingVSnapshot\");\n }\n }),\n addEventListener5(this._tableSnapshotToggle, \"click\", () => {\n if (!this._tableSnapshotToggle.classList.contains(\"disabled\"))\n this._recorder.setMode(this._recorder.state.mode === \"assertingTableCell\" ? \"recording\" : \"assertingTableCell\");\n }),\n addEventListener5(this._fileUploadToggle, \"click\", () => {\n if (!this._fileUploadToggle.classList.contains(\"disabled\")) {\n this._recorder.setMode(this._recorder.state.mode === \"recordingFileUpload\" ? \"recording\" : \"recordingFileUpload\");\n }\n }),\n addEventListener5(this._pointerEventsToggle, \"click\", () => {\n this._recorder.togglePointerEventsOverride();\n }),\n // addEventListener(this._jsonMarkerButton, 'click', () => {\n // if (this._jsonMarkerButton.classList.contains('disabled'))\n // return;\n // // console.log('fetch Custom JSON button clicked');\n // const sequence = Math.floor(Math.random() * 1000000);\n // // Record the marker action\n // const markerAction: actions.Action = {\n // name: 'marker',\n // timestamp: getTimestamp(this._recorder),\n // sequence: sequence,\n // signals: [],\n // };\n // this._recorder.recordAction(markerAction);\n // fetch('http://localhost:35142/skyramp/deploy/tracemarker')\n // .catch(error => {/* console.log(error) */});\n // }),\n addEventListener5(this._modularityToggle, \"click\", () => {\n if (this._modularityToggle.classList.contains(\"disabled\"))\n return;\n this._recorder.modularityToggled = !this._recorder.modularityToggled;\n let sectionBoundary = \"endBlock\";\n if (this._recorder.modularityToggled) {\n sectionBoundary = \"beginBlock\";\n }\n const modularAction = {\n name: sectionBoundary,\n timestamp: getTimestamp10(this._recorder),\n sequence: Math.floor(Math.random() * 1e6),\n signals: []\n };\n this._recorder.recordAction(modularAction);\n }),\n addEventListener5(this._assertApiPayloadToggle, \"click\", () => {\n if (this._assertApiPayloadToggle.classList.contains(\"disabled\"))\n return;\n const assertApiPayloadAction = {\n name: \"assertApiRequest\",\n timestamp: getTimestamp10(this._recorder),\n signals: []\n };\n this._recorder.recordAction(assertApiPayloadAction);\n this._assertApiPayloadToggle.classList.add(\"toggled\");\n setTimeout(() => this._assertApiPayloadToggle.classList.remove(\"toggled\"), 1500);\n }),\n addEventListener5(this._dragRecordToggle, \"click\", () => {\n if (this._dragRecordToggle.classList.contains(\"disabled\"))\n return;\n this._recorder.setMode(this._recorder.state.mode === \"recordingDrag\" ? \"recording\" : \"recordingDrag\");\n }),\n addEventListener5(this._areaSelectToggle, \"click\", () => {\n const mode = this._recorder.state.mode;\n if (mode === \"recordingArea\") {\n this._recorder.setMode(\"recording\");\n } else {\n this._recorder.setMode(\"recordingArea\");\n }\n }),\n addEventListener5(this._sketchToolToggle, \"click\", () => {\n if (!this._sketchToolToggle.classList.contains(\"disabled\")) {\n const mode = this._recorder.state.mode;\n if (mode === \"recordingSketchTool\") {\n this._recorder.setMode(\"recording\");\n } else {\n this._recorder.setMode(\"recordingSketchTool\");\n }\n }\n }),\n addEventListener5(this._gojsLinkToggle, \"click\", () => {\n if (!this._gojsLinkToggle.classList.contains(\"disabled\")) {\n const mode = this._recorder.state.mode;\n this._recorder.setMode(mode === \"recordingGoJSLink\" ? \"recording\" : \"recordingGoJSLink\");\n }\n })\n ];\n }\n install() {\n this._recorder.highlight.appendChild(this._overlayElement);\n this._refreshListeners();\n this._updateVisualPosition();\n const consumeEvent6 = (e) => {\n const target = e.target;\n if (target && (target === this._dragHandle || this._dragHandle.contains(target))) {\n if (e.type === \"mousedown\" || e.type === \"mousemove\" || e.type === \"mouseup\" || e.type === \"pointerdown\" || e.type === \"pointermove\" || e.type === \"pointerup\") {\n return;\n }\n }\n e.stopPropagation();\n e.preventDefault();\n };\n this._listeners.push(\n addEventListener5(this._overlayElement, \"mousedown\", consumeEvent6, false),\n addEventListener5(this._overlayElement, \"mouseup\", consumeEvent6, false),\n addEventListener5(this._overlayElement, \"mousemove\", consumeEvent6, false),\n addEventListener5(this._overlayElement, \"pointerdown\", consumeEvent6, false),\n addEventListener5(this._overlayElement, \"pointerup\", consumeEvent6, false),\n addEventListener5(this._overlayElement, \"pointermove\", consumeEvent6, false),\n addEventListener5(this._overlayElement, \"click\", consumeEvent6, false),\n addEventListener5(this._overlayElement, \"dblclick\", consumeEvent6, false),\n addEventListener5(this._overlayElement, \"contextmenu\", consumeEvent6, false),\n addEventListener5(this._overlayElement, \"focus\", consumeEvent6, false),\n addEventListener5(this._overlayElement, \"blur\", consumeEvent6, false)\n );\n }\n contains(element) {\n return this._recorder.injectedScript.utils.isInsideScope(this._overlayElement, element);\n }\n setUIState(state) {\n const isRecording = state.mode === \"recording\" || state.mode === \"assertingText\" || state.mode === \"assertingVisibility\" || state.mode === \"assertingValue\" || state.mode === \"assertingSnapshot\" || state.mode === \"assertingVSnapshot\" || state.mode === \"assertingTableCell\" || state.mode === \"recording-inspecting\" || state.mode === \"recordingDrag\" || state.mode === \"recordingGoJSLink\" || state.mode === \"recordingArea\" || state.mode === \"recordingFileUpload\" || state.mode === \"recordingSketchTool\";\n this._recordToggle.classList.toggle(\"toggled\", isRecording);\n this._recordToggle.title = isRecording ? \"Stop Recording\" : \"Start Recording\";\n this._pickLocatorToggle.classList.toggle(\"toggled\", state.mode === \"inspecting\" || state.mode === \"recording-inspecting\");\n this._pickLocatorToggle.classList.toggle(\"disabled\", state.mode === \"recordingArea\");\n this._assertVisibilityToggle.classList.toggle(\"toggled\", state.mode === \"assertingVisibility\");\n this._assertVisibilityToggle.classList.toggle(\"disabled\", state.mode === \"none\" || state.mode === \"standby\" || state.mode === \"inspecting\" || state.mode === \"recordingArea\");\n this._assertTextToggle.classList.toggle(\"toggled\", state.mode === \"assertingText\");\n this._assertTextToggle.classList.toggle(\"disabled\", state.mode === \"none\" || state.mode === \"standby\" || state.mode === \"inspecting\" || state.mode === \"recordingArea\");\n this._assertValuesToggle.classList.toggle(\"toggled\", state.mode === \"assertingValue\");\n this._assertValuesToggle.classList.toggle(\"disabled\", state.mode === \"none\" || state.mode === \"standby\" || state.mode === \"inspecting\" || state.mode === \"recordingArea\");\n this._assertSnapshotToggle.classList.toggle(\"toggled\", state.mode === \"assertingSnapshot\");\n this._assertSnapshotToggle.classList.toggle(\"disabled\", state.mode === \"none\" || state.mode === \"standby\" || state.mode === \"inspecting\" || state.mode === \"recordingArea\");\n this._assertVSnapshotToggle.classList.toggle(\"toggled\", state.mode === \"assertingVSnapshot\");\n this._assertVSnapshotToggle.classList.toggle(\"disabled\", state.mode === \"none\" || state.mode === \"standby\" || state.mode === \"inspecting\" || state.mode === \"recordingArea\");\n this._tableSnapshotToggle.classList.toggle(\"toggled\", state.mode === \"assertingTableCell\");\n this._tableSnapshotToggle.classList.toggle(\"disabled\", state.mode === \"none\" || state.mode === \"standby\" || state.mode === \"inspecting\" || state.mode === \"recordingArea\");\n this._fileUploadToggle.classList.toggle(\"toggled\", state.mode === \"recordingFileUpload\");\n this._fileUploadToggle.classList.toggle(\"disabled\", state.mode === \"none\" || state.mode === \"standby\" || state.mode === \"inspecting\" || state.mode === \"recordingArea\");\n this._modularityToggle.classList.toggle(\"disabled\", state.mode === \"none\" || state.mode === \"standby\" || state.mode === \"inspecting\" || state.mode === \"recordingArea\");\n this._assertApiPayloadToggle.classList.toggle(\"disabled\", state.mode === \"none\" || state.mode === \"standby\" || state.mode === \"inspecting\" || state.mode === \"recordingArea\");\n this._dragRecordToggle.classList.toggle(\"toggled\", state.mode === \"recordingDrag\");\n this._dragRecordToggle.classList.toggle(\"disabled\", state.mode === \"none\" || state.mode === \"standby\" || state.mode === \"inspecting\" || state.mode === \"recordingArea\");\n this._areaSelectToggle.classList.toggle(\"toggled\", state.mode === \"recordingArea\");\n this._areaSelectToggle.classList.toggle(\"disabled\", state.mode === \"none\");\n this._sketchToolToggle.classList.toggle(\"toggled\", state.mode === \"recordingSketchTool\");\n this._sketchToolToggle.classList.toggle(\"disabled\", state.mode === \"none\" || state.mode === \"standby\" || state.mode === \"inspecting\" || state.mode === \"recordingArea\");\n this._gojsLinkToggle.classList.toggle(\"toggled\", state.mode === \"recordingGoJSLink\");\n this._gojsLinkToggle.classList.toggle(\"disabled\", state.mode === \"none\" || state.mode === \"standby\" || state.mode === \"inspecting\" || state.mode === \"recordingArea\");\n this.updateToolbar();\n if (this._offsetX !== state.overlay.offsetX) {\n this._offsetX = state.overlay.offsetX;\n this._updateVisualPosition();\n }\n if (state.mode === \"none\")\n this._hideOverlay();\n else\n this._showOverlay();\n }\n updateToolbar() {\n this._pointerEventsToggle.classList.toggle(\"toggled\", this._recorder.pointerEventsOverrideEnabled);\n }\n flashToolSucceeded(tool) {\n let element;\n if (tool === \"assertingVisibility\")\n element = this._assertVisibilityToggle;\n else if (tool === \"assertingSnapshot\")\n element = this._assertSnapshotToggle;\n else if (tool === \"assertingVSnapshot\")\n element = this._assertVSnapshotToggle;\n else if (tool === \"assertingTableCell\")\n element = this._tableSnapshotToggle;\n else if (tool === \"fileUpload\")\n element = this._fileUploadToggle;\n else if (tool === \"recordingArea\")\n element = this._areaSelectToggle;\n else if (tool === \"recordingTableSnapshot\")\n element = this._tableSnapshotToggle;\n else if (tool === \"recordingSketchTool\")\n element = this._sketchToolToggle;\n else\n element = this._assertValuesToggle;\n element.classList.add(\"succeeded\");\n this._recorder.injectedScript.utils.builtins.setTimeout(() => element.classList.remove(\"succeeded\"), 2e3);\n }\n _hideOverlay() {\n this._overlayElement.setAttribute(\"hidden\", \"true\");\n }\n _showOverlay() {\n if (!this._overlayElement.hasAttribute(\"hidden\"))\n return;\n this._overlayElement.removeAttribute(\"hidden\");\n this._updateVisualPosition();\n }\n _updateVisualPosition() {\n this._measure = this._overlayElement.getBoundingClientRect();\n this._overlayElement.style.left = (this._recorder.injectedScript.window.innerWidth - this._measure.width) / 2 + this._offsetX + \"px\";\n }\n onMouseMove(event) {\n if (!event.buttons) {\n this._dragState = void 0;\n return false;\n }\n if (this._dragState) {\n this._offsetX = this._dragState.offsetX + event.clientX - this._dragState.dragStart.x;\n const halfGapSize = (this._recorder.injectedScript.window.innerWidth - this._measure.width) / 2 - 10;\n this._offsetX = Math.max(-halfGapSize, Math.min(halfGapSize, this._offsetX));\n this._updateVisualPosition();\n this._recorder.setOverlayState({ offsetX: this._offsetX });\n consumeEvent5(event);\n return true;\n }\n return false;\n }\n onMouseUp(event) {\n if (this._dragState) {\n consumeEvent5(event);\n return true;\n }\n return false;\n }\n onClick(event) {\n if (this._dragState) {\n this._dragState = void 0;\n consumeEvent5(event);\n return true;\n }\n return false;\n }\n onDblClick(event) {\n return false;\n }\n // method to update modularity toggle state in the UI\n updateModularityToggleState(toggled) {\n this._modularityToggle.classList.toggle(\"toggled\", toggled);\n }\n};\nvar _Recorder = class _Recorder {\n constructor(injectedScript, options) {\n this._listeners = [];\n this._lastHighlightedSelector = void 0;\n this._lastHighlightedAriaTemplateJSON = \"undefined\";\n this.state = {\n mode: \"none\",\n testIdAttributeName: \"data-testid\",\n language: \"javascript\",\n overlay: { offsetX: 0 },\n modularityToggled: false\n };\n this._delegate = {};\n this._modularityToggled = false;\n // SKYR-3747: short-lived buffer of recently hovered, \"menu-trigger-like\" elements.\n // Used to synthesize a hover action before a click whose preconditionSelector\n // points to a freshly-revealed flyout container (Cisco XDR Client Mgmt → Profiles).\n this._recentHoverTrail = [];\n // SKYR-3781: previous recorded user action (see _maybeEmitFlyoutHoverBeforeClick).\n this._previousUserActionName = void 0;\n var _a, _b;\n this.document = injectedScript.document;\n this.injectedScript = injectedScript;\n this.highlight = injectedScript.createHighlight();\n this._nestedElementHandler = new NestedElementHandler(this.document);\n this._modalHandler = new ModalHandler(this.document);\n this._modalHandler.setOnModalOpen(({ selector }) => {\n this.recordAction({\n name: \"modalOpen\",\n selector,\n signals: [],\n timestamp: getTimestamp10(this)\n });\n });\n this._modalHandler.setOnModalClose(({ selector }) => {\n this.recordAction({\n name: \"modalClose\",\n selector,\n signals: [],\n timestamp: getTimestamp10(this)\n });\n });\n this._iframeHandler = new IframeHandler(this.document);\n this._iframeHandler.setOnIframeLoad(({ selector }) => {\n this.recordAction({\n name: \"iframeLoad\",\n selector,\n signals: [],\n timestamp: getTimestamp10(this)\n });\n });\n this._tools = {\n \"none\": new NoneTool(),\n \"standby\": new NoneTool(),\n \"inspecting\": new InspectTool(this, false),\n \"recording\": (options == null ? void 0 : options.recorderMode) === \"api\" ? new JsonRecordActionTool(this) : new RecordActionTool(this),\n \"recording-inspecting\": new InspectTool(this, false),\n \"assertingText\": new TextAssertionTool(this, \"text\"),\n \"assertingVisibility\": new InspectTool(this, true),\n \"assertingValue\": new TextAssertionTool(this, \"value\"),\n \"assertingSnapshot\": new TextAssertionTool(this, \"snapshot\"),\n \"assertingVSnapshot\": new VisualSnapshotTool(this),\n \"assertingTableCell\": new TableAssertTool(this),\n \"recordingDrag\": new DragDropTool(this),\n \"recordingGoJSLink\": new GoJSLinkTool(this),\n \"recordingArea\": new AreaSelectionTool(this),\n \"recordingFileUpload\": new FileUploadTool(this),\n \"recordingTableSnapshot\": new TableSnapshotTool(this),\n \"recordingDomSnapshot\": new DomSnapshotTool(this),\n \"recordingSketchTool\": new SketchTool(this),\n \"replaying\": new NoneTool()\n };\n this._currentTool = this._tools.none;\n (_b = (_a = this._currentTool).install) == null ? void 0 : _b.call(_a);\n if (injectedScript.window.top === injectedScript.window && (options == null ? void 0 : options.recorderMode) !== \"api\") {\n this.overlay = new Overlay(this);\n this.overlay.setUIState(this.state);\n }\n this._stylesheet = new injectedScript.window.CSSStyleSheet();\n this._stylesheet.replaceSync(`\n body[data-pw-cursor=pointer] *, body[data-pw-cursor=pointer] *::after { cursor: pointer !important; }\n body[data-pw-cursor=text] *, body[data-pw-cursor=text] *::after { cursor: text !important; }\n body[data-pw-cursor=crosshair] *, body[data-pw-cursor=crosshair] *::after { cursor: crosshair !important; }\n body[data-pw-cursor=grab] *, body[data-pw-cursor=grab] *::after { cursor: grab !important; }\n `);\n this.installListeners();\n this._installFileUploadHooks();\n injectedScript.utils.cacheNormalizedWhitespaces();\n if (injectedScript.isUnderTest) {\n console.error(\"Recorder script ready for test\");\n injectedScript.window.__pw_recorderToggleNestedElements = () => {\n this.togglePointerEventsOverride();\n };\n }\n injectedScript.consoleApi.install();\n }\n get modularityToggled() {\n return this._modularityToggled;\n }\n set modularityToggled(value) {\n this._modularityToggled = value;\n try {\n if (typeof window.__pw_recorderSetModularityToggled === \"function\") {\n window.__pw_recorderSetModularityToggled(value);\n } else {\n console.warn(\"Modularity toggle binding not available yet, state may be out of sync\");\n }\n } catch (e) {\n console.error(\"Failed to set modularity toggle on server:\", e);\n }\n }\n get pointerEventsOverrideEnabled() {\n return this._nestedElementHandler.enabled;\n }\n togglePointerEventsOverride() {\n var _a;\n this._nestedElementHandler.toggle();\n (_a = this.overlay) == null ? void 0 : _a.updateToolbar();\n }\n installListeners() {\n var _a, _b;\n removeEventListeners3(this._listeners);\n this._listeners = [\n addEventListener5(this.document, \"click\", (event) => this._onClick(event), true),\n addEventListener5(this.document, \"auxclick\", (event) => this._onClick(event), true),\n addEventListener5(this.document, \"dblclick\", (event) => this._onDblClick(event), true),\n addEventListener5(this.document, \"contextmenu\", (event) => this._onContextMenu(event), true),\n addEventListener5(this.document, \"dragstart\", (event) => this._onDragStart(event), true),\n addEventListener5(this.document, \"input\", (event) => this._onInput(event), true),\n addEventListener5(this.document, \"keydown\", (event) => this._onKeyDown(event), true),\n addEventListener5(this.document, \"keyup\", (event) => this._onKeyUp(event), true),\n addEventListener5(this.document, \"pointerdown\", (event) => this._onPointerDown(event), true),\n addEventListener5(this.document, \"pointermove\", (event) => this._onPointerMove(event), true),\n addEventListener5(this.document, \"pointerup\", (event) => this._onPointerUp(event), true),\n addEventListener5(this.document, \"mousedown\", (event) => this._onMouseDown(event), true),\n addEventListener5(this.document, \"mouseup\", (event) => this._onMouseUp(event), true),\n addEventListener5(this.document, \"mousemove\", (event) => this._onMouseMove(event), true),\n addEventListener5(this.document, \"mouseleave\", (event) => this._onMouseLeave(event), true),\n addEventListener5(this.document, \"mouseenter\", (event) => this._onMouseEnter(event), true),\n addEventListener5(this.document, \"focus\", (event) => this._onFocus(event), true),\n addEventListener5(this.document, \"scroll\", (event) => this._onScroll(event), true)\n ];\n this.highlight.install();\n let recreationInterval;\n const recreate = () => {\n this.highlight.install();\n if (this.overlay) {\n const overlayElement = this.overlay._overlayElement;\n const glassPaneElement = this.highlight._glassPaneElement;\n const glassPaneConnected = glassPaneElement && glassPaneElement.isConnected;\n const overlayDisconnected = overlayElement && !overlayElement.isConnected;\n if (glassPaneConnected && overlayDisconnected) {\n this.overlay.install();\n }\n }\n recreationInterval = this.injectedScript.utils.builtins.setTimeout(recreate, 500);\n };\n recreationInterval = this.injectedScript.utils.builtins.setTimeout(recreate, 500);\n this._listeners.push(() => this.injectedScript.utils.builtins.clearTimeout(recreationInterval));\n this.highlight.appendChild(createSvgElement(this.document, clipPaths_default));\n if (this.overlay) {\n const glassPaneElement = this.highlight._glassPaneElement;\n if (glassPaneElement && glassPaneElement.isConnected) {\n this.overlay.install();\n }\n }\n (_b = (_a = this._currentTool) == null ? void 0 : _a.install) == null ? void 0 : _b.call(_a);\n this.document.adoptedStyleSheets.push(this._stylesheet);\n }\n _installFileUploadHooks() {\n installFileUploadHooks(this, this._listeners);\n }\n _switchCurrentTool() {\n var _a, _b, _c, _d, _e, _f, _g, _h;\n const newTool = this._tools[this.state.mode];\n if (newTool === this._currentTool)\n return;\n (_b = (_a = this._currentTool).uninstall) == null ? void 0 : _b.call(_a);\n this.clearHighlight();\n this._currentTool = newTool;\n (_d = (_c = this._currentTool).install) == null ? void 0 : _d.call(_c);\n if (this.state.mode === \"recording\") {\n const activeEl = deepActiveElement(this.document);\n if (activeEl && activeEl !== this.document.body && activeEl !== this.document.documentElement)\n (_f = (_e = this._currentTool).onFocus) == null ? void 0 : _f.call(_e, new FocusEvent(\"focus\"));\n }\n const cursor = (_g = newTool.cursor) == null ? void 0 : _g.call(newTool);\n if (cursor)\n (_h = this.injectedScript.document.body) == null ? void 0 : _h.setAttribute(\"data-pw-cursor\", cursor);\n }\n setUIState(state, delegate) {\n var _a, _b;\n this._delegate = delegate;\n if (state.actionPoint && this.state.actionPoint && state.actionPoint.x === this.state.actionPoint.x && state.actionPoint.y === this.state.actionPoint.y) {\n } else if (!state.actionPoint && !this.state.actionPoint) {\n } else {\n if (state.actionPoint)\n this.highlight.showActionPoint(state.actionPoint.x, state.actionPoint.y);\n else\n this.highlight.hideActionPoint();\n }\n if (state.modularityToggled !== this._modularityToggled) {\n this._modularityToggled = state.modularityToggled;\n (_a = this.overlay) == null ? void 0 : _a.updateModularityToggleState(this._modularityToggled);\n }\n this.state = state;\n this.highlight.setLanguage(state.language);\n this._switchCurrentTool();\n (_b = this.overlay) == null ? void 0 : _b.setUIState(state);\n if (state.mode === \"recording\") {\n this._modalHandler.enable();\n this._iframeHandler.enable();\n } else {\n this._modalHandler.disable();\n this._iframeHandler.disable();\n }\n let highlight = \"noop\";\n if (state.actionSelector !== this._lastHighlightedSelector) {\n const entries = state.actionSelector ? entriesForSelectorHighlight(this.injectedScript, state.language, state.actionSelector, this.document) : null;\n highlight = (entries == null ? void 0 : entries.length) ? entries : \"clear\";\n this._lastHighlightedSelector = (entries == null ? void 0 : entries.length) ? state.actionSelector : void 0;\n }\n const ariaTemplateJSON = JSON.stringify(state.ariaTemplate);\n if (this._lastHighlightedAriaTemplateJSON !== ariaTemplateJSON) {\n const elements = state.ariaTemplate ? this.injectedScript.getAllElementsMatchingExpectAriaTemplate(this.document, state.ariaTemplate) : [];\n if (elements.length) {\n const color = elements.length > 1 ? HighlightColors2.multiple : HighlightColors2.single;\n highlight = elements.map((element) => ({ element, color }));\n this._lastHighlightedAriaTemplateJSON = ariaTemplateJSON;\n } else {\n if (!this._lastHighlightedSelector)\n highlight = \"clear\";\n this._lastHighlightedAriaTemplateJSON = \"undefined\";\n }\n }\n if (highlight === \"clear\")\n this.highlight.clearHighlight();\n else if (highlight !== \"noop\")\n this.highlight.updateHighlight(highlight);\n }\n clearHighlight() {\n this.updateHighlight(null, false);\n }\n _onClick(event) {\n var _a, _b, _c;\n if (!event.isTrusted)\n return;\n if ((_a = this.overlay) == null ? void 0 : _a.onClick(event))\n return;\n if (this._ignoreOverlayEvent(event))\n return;\n (_c = (_b = this._currentTool).onClick) == null ? void 0 : _c.call(_b, event);\n }\n _onDblClick(event) {\n var _a, _b, _c;\n if (!event.isTrusted)\n return;\n if ((_a = this.overlay) == null ? void 0 : _a.onDblClick(event))\n return;\n if (this._ignoreOverlayEvent(event))\n return;\n (_c = (_b = this._currentTool).onDblClick) == null ? void 0 : _c.call(_b, event);\n }\n _onContextMenu(event) {\n var _a, _b;\n if (!event.isTrusted)\n return;\n (_b = (_a = this._currentTool).onContextMenu) == null ? void 0 : _b.call(_a, event);\n }\n _onDragStart(event) {\n var _a, _b;\n if (!event.isTrusted)\n return;\n if (this._ignoreOverlayEvent(event))\n return;\n (_b = (_a = this._currentTool).onDragStart) == null ? void 0 : _b.call(_a, event);\n }\n _onPointerDown(event) {\n var _a, _b;\n if (!event.isTrusted)\n return;\n if (this._ignoreOverlayEvent(event))\n return;\n (_b = (_a = this._currentTool).onPointerDown) == null ? void 0 : _b.call(_a, event);\n }\n _onPointerUp(event) {\n var _a, _b;\n if (!event.isTrusted)\n return;\n if (this._ignoreOverlayEvent(event))\n return;\n (_b = (_a = this._currentTool).onPointerUp) == null ? void 0 : _b.call(_a, event);\n }\n _onPointerMove(event) {\n var _a, _b;\n if (!event.isTrusted)\n return;\n if (this._ignoreOverlayEvent(event))\n return;\n (_b = (_a = this._currentTool).onPointerMove) == null ? void 0 : _b.call(_a, event);\n }\n _onMouseDown(event) {\n var _a, _b;\n if (!event.isTrusted)\n return;\n if (this._ignoreOverlayEvent(event))\n return;\n (_b = (_a = this._currentTool).onMouseDown) == null ? void 0 : _b.call(_a, event);\n }\n _onMouseUp(event) {\n var _a, _b, _c;\n if (!event.isTrusted)\n return;\n if ((_a = this.overlay) == null ? void 0 : _a.onMouseUp(event))\n return;\n if (this._ignoreOverlayEvent(event))\n return;\n (_c = (_b = this._currentTool).onMouseUp) == null ? void 0 : _c.call(_b, event);\n }\n _onMouseMove(event) {\n var _a, _b, _c;\n if (!event.isTrusted)\n return;\n if ((_a = this.overlay) == null ? void 0 : _a.onMouseMove(event))\n return;\n if (this._ignoreOverlayEvent(event))\n return;\n this._trackHoverTrail(event);\n (_c = (_b = this._currentTool).onMouseMove) == null ? void 0 : _c.call(_b, event);\n }\n _onMouseEnter(event) {\n var _a, _b;\n if (!event.isTrusted)\n return;\n if (this._ignoreOverlayEvent(event))\n return;\n (_b = (_a = this._currentTool).onMouseEnter) == null ? void 0 : _b.call(_a, event);\n }\n // SKYR-3747: append the closest menu-trigger ancestor of the pointer target\n // to the hover trail. We only track menu-trigger-like elements, dedupe\n // against the previous entry, and bound the buffer by size and age so\n // lookups stay cheap and stale entries don't survive across user flows.\n _trackHoverTrail(event) {\n if (this.state.mode !== \"recording\")\n return;\n const initial = this.deepEventTarget(event);\n if (!initial)\n return;\n const trigger = closestMenuTrigger(initial, this.document);\n if (!trigger)\n return;\n const now = performance.now();\n const last = this._recentHoverTrail[this._recentHoverTrail.length - 1];\n if (last && last.element === trigger)\n return;\n this._recentHoverTrail.push({ element: trigger, timestamp: now });\n const cutoff = now - _Recorder._HOVER_TRAIL_LOOKBACK_MS;\n while (this._recentHoverTrail.length > 0 && this._recentHoverTrail[0].timestamp < cutoff)\n this._recentHoverTrail.shift();\n if (this._recentHoverTrail.length > _Recorder._HOVER_TRAIL_MAX)\n this._recentHoverTrail.splice(0, this._recentHoverTrail.length - _Recorder._HOVER_TRAIL_MAX);\n }\n _onMouseLeave(event) {\n var _a, _b;\n if (!event.isTrusted)\n return;\n if (this._ignoreOverlayEvent(event))\n return;\n (_b = (_a = this._currentTool).onMouseLeave) == null ? void 0 : _b.call(_a, event);\n }\n _onFocus(event) {\n var _a, _b;\n if (this._ignoreOverlayEvent(event))\n return;\n (_b = (_a = this._currentTool).onFocus) == null ? void 0 : _b.call(_a, event);\n }\n _onScroll(event) {\n var _a, _b;\n if (!event.isTrusted)\n return;\n this._lastHighlightedSelector = void 0;\n this._lastHighlightedAriaTemplateJSON = \"undefined\";\n this.highlight.hideActionPoint();\n (_b = (_a = this._currentTool).onScroll) == null ? void 0 : _b.call(_a, event);\n }\n _onInput(event) {\n var _a, _b;\n if (!event.isTrusted)\n return;\n if (this._ignoreOverlayEvent(event))\n return;\n (_b = (_a = this._currentTool).onInput) == null ? void 0 : _b.call(_a, event);\n }\n _onKeyDown(event) {\n var _a, _b;\n if (!event.isTrusted)\n return;\n if (this._ignoreOverlayEvent(event))\n return;\n (_b = (_a = this._currentTool).onKeyDown) == null ? void 0 : _b.call(_a, event);\n }\n _onKeyUp(event) {\n var _a, _b;\n if (!event.isTrusted)\n return;\n if (this._ignoreOverlayEvent(event))\n return;\n (_b = (_a = this._currentTool).onKeyUp) == null ? void 0 : _b.call(_a, event);\n }\n updateHighlight(model, userGesture) {\n this._lastHighlightedSelector = void 0;\n this._lastHighlightedAriaTemplateJSON = \"undefined\";\n this._updateHighlight(model, userGesture);\n }\n _updateHighlight(model, userGesture) {\n var _a, _b;\n let tooltipText = model == null ? void 0 : model.tooltipText;\n if (tooltipText === void 0 && (model == null ? void 0 : model.selector))\n tooltipText = this.injectedScript.utils.asLocator(this.state.language, model.selector);\n if (model)\n this.highlight.updateHighlight(model.elements.map((element) => ({ element, color: model.color, tooltipText })));\n else\n this.highlight.clearHighlight();\n if (userGesture)\n (_b = (_a = this._delegate).highlightUpdated) == null ? void 0 : _b.call(_a);\n }\n _ignoreOverlayEvent(event) {\n return event.composedPath().some((e) => {\n const nodeName = e.nodeName || \"\";\n return nodeName.toLowerCase() === \"x-pw-glass\";\n });\n }\n deepEventTarget(event) {\n var _a;\n for (const element of event.composedPath()) {\n if (!((_a = this.overlay) == null ? void 0 : _a.contains(element)))\n return element;\n }\n return event.composedPath()[0];\n }\n setMode(mode) {\n var _a, _b;\n void ((_b = (_a = this._delegate).setMode) == null ? void 0 : _b.call(_a, mode));\n }\n _captureAutoExpectSnapshot() {\n const documentElement = this.injectedScript.document.documentElement;\n return documentElement ? this.injectedScript.utils.generateAriaTree(documentElement, { mode: \"autoexpect\" }) : void 0;\n }\n async performAction(action) {\n var _a, _b;\n this._decorateUserAction(action);\n await ((_b = (_a = this._delegate).performAction) == null ? void 0 : _b.call(_a, action).catch(() => {\n }));\n }\n // Updates the aria-snapshot baseline, computes preconditionSelector for\n // non-assert actions, and (for clicks) synthesizes a hover action when the\n // click target lives inside a freshly-revealed flyout container (SKYR-3747).\n // The synthetic hover is fire-and-forget so the caller can dispatch the\n // primary action immediately afterward — important for <a href> clicks that\n // tear down the page before any awaited recordAction round-trip resolves.\n _decorateUserAction(action) {\n var _a;\n const previousSnapshot = this._lastActionAutoexpectSnapshot;\n this._lastActionAutoexpectSnapshot = this._captureAutoExpectSnapshot();\n if (isAssertAction(action) || !this._lastActionAutoexpectSnapshot)\n return;\n const revealedElement = this.injectedScript.utils.findNewElement(previousSnapshot == null ? void 0 : previousSnapshot.root, (_a = this._lastActionAutoexpectSnapshot) == null ? void 0 : _a.root);\n if (!(\"preconditionSelector\" in action) || action.preconditionSelector === void 0) {\n const withSelector = action;\n withSelector.preconditionSelector = revealedElement ? this.injectedScript.generateSelector(revealedElement, { testIdAttributeName: this.state.testIdAttributeName }).selector : void 0;\n if (\"selector\" in action && withSelector.preconditionSelector === withSelector.selector)\n withSelector.preconditionSelector = void 0;\n }\n if (action.name === \"click\" && action.preconditionSelector && revealedElement)\n this._maybeEmitFlyoutHoverBeforeClick(action, revealedElement);\n if (action.name === \"click\")\n this._maybeEmitRowRevealHoverBeforeClick(action);\n this._previousUserActionName = action.name;\n }\n // SKYR-3744 Gap 5 / SKYR-3781: when a click lands on a per-row action control\n // (button/menuitem) inside a row-like container, emit a hover on the row\n // first so replay reproduces the CSS :hover that revealed the control (Box\n // \"More Options <name>\"). Lives here (not in a tool) so it runs in both the\n // default and api recorder modes, and is decided from the DOM at click time\n // — not from a mousemove snapshot — so it fires even when the cursor lands on\n // the control after a navigation with no intervening mousemove into the row.\n // That determinism is what makes codegen output replay-clean without edits.\n _maybeEmitRowRevealHoverBeforeClick(clickAction) {\n var _a, _b;\n let clickTarget = null;\n try {\n const parsed = this.injectedScript.parseSelector(clickAction.selector);\n clickTarget = (_a = this.injectedScript.querySelectorAll(parsed, this.document)[0]) != null ? _a : null;\n } catch {\n return;\n }\n if (!clickTarget)\n return;\n const container = clickTarget.closest(_Recorder._REVEAL_CONTAINER_SELECTOR);\n if (!container)\n return;\n const control = clickTarget.closest('button, [role=\"button\"], [role=\"menuitem\"]');\n if (!control || !container.contains(control))\n return;\n const hoverTargetElement = (_b = this._findRevealHoverTarget(container)) != null ? _b : container;\n let selector;\n try {\n selector = this.injectedScript.generateSelector(hoverTargetElement, { testIdAttributeName: this.state.testIdAttributeName }).selector;\n } catch {\n return;\n }\n if (!selector)\n return;\n const clickTs = parseInt(clickAction.timestamp, 10);\n const hoverAction = {\n name: \"hover\",\n selector,\n signals: [],\n timestamp: Number.isFinite(clickTs) ? (clickTs - 1).toString() : clickAction.timestamp\n };\n if (this._delegate.recordAction)\n void this._delegate.recordAction(hoverAction).catch(() => {\n });\n }\n // SKYR-3744: most stable distinguishing descendant of a row-like container —\n // a link/heading with a short, view-agnostic accessible name (the item's own\n // name like \"test123\"), falling back to null. See _maybeEmitRowRevealHoverBeforeClick.\n _findRevealHoverTarget(container) {\n const candidates = container.querySelectorAll('a[href], [role=\"link\"], h1, h2, h3, h4, h5, h6');\n for (const el of Array.from(candidates)) {\n const text = (el.innerText || el.textContent || \"\").trim();\n if (!text || text.length > 80)\n continue;\n return el;\n }\n return null;\n }\n _maybeEmitFlyoutHoverBeforeClick(clickAction, revealedElement) {\n var _a;\n if (this._recentHoverTrail.length === 0)\n return;\n if (this._previousUserActionName === \"click\")\n return;\n let clickTarget = null;\n try {\n const parsed = this.injectedScript.parseSelector(clickAction.selector);\n const matches = this.injectedScript.querySelectorAll(parsed, this.document);\n clickTarget = (_a = matches[0]) != null ? _a : null;\n } catch {\n return;\n }\n if (!clickTarget || !revealedElement.contains(clickTarget))\n return;\n for (let i = this._recentHoverTrail.length - 1; i >= 0; i--) {\n const candidate = this._recentHoverTrail[i].element;\n if (!candidate.isConnected)\n continue;\n if (candidate === clickTarget)\n continue;\n if (clickTarget.contains(candidate) || candidate.contains(clickTarget))\n continue;\n if (revealedElement.contains(candidate) || candidate.contains(revealedElement))\n continue;\n const generated = this.injectedScript.generateSelector(candidate, { testIdAttributeName: this.state.testIdAttributeName });\n if (!generated.selector)\n continue;\n const clickTs = parseInt(clickAction.timestamp, 10);\n const hoverAction = {\n name: \"hover\",\n selector: generated.selector,\n signals: [],\n timestamp: Number.isFinite(clickTs) ? (clickTs - 1).toString() : clickAction.timestamp\n };\n if (this._delegate.recordAction)\n void this._delegate.recordAction(hoverAction).catch(() => {\n });\n this._recentHoverTrail = [];\n return;\n }\n }\n recordAction(action) {\n this._decorateUserAction(action);\n if (this._delegate.recordAction) {\n void this._delegate.recordAction(action);\n } else {\n console.warn(\"[Recorder] No delegate.recordAction available!\");\n }\n }\n setOverlayState(state) {\n var _a, _b;\n void ((_b = (_a = this._delegate).setOverlayState) == null ? void 0 : _b.call(_a, state));\n }\n elementPicked(selector, model) {\n var _a, _b;\n const ariaSnapshot = this.injectedScript.ariaSnapshot(model.elements[0], { mode: \"expect\" });\n void ((_b = (_a = this._delegate).elementPicked) == null ? void 0 : _b.call(_a, { selector, ariaSnapshot }));\n }\n};\n_Recorder._HOVER_TRAIL_MAX = 20;\n_Recorder._HOVER_TRAIL_LOOKBACK_MS = 1e4;\n// SKYR-3744 Gap 5: row-like containers whose per-row action controls are\n// revealed only on hover (Box files grid, Linear/GitHub row actions, …).\n_Recorder._REVEAL_CONTAINER_SELECTOR = '[role=\"row\"], [data-testid=\"grid-view-item\"], [data-testid$=\"-item\"], [data-testid$=\"-row\"], [draggable=\"true\"], tr';\nvar Recorder = _Recorder;\nvar Dialog = class {\n constructor(recorder) {\n this._dialogElement = null;\n this._recorder = recorder;\n }\n isShowing() {\n return !!this._dialogElement;\n }\n show(options) {\n const acceptButton = this._recorder.document.createElement(\"x-pw-tool-item\");\n acceptButton.title = \"Accept\";\n acceptButton.classList.add(\"accept\");\n acceptButton.appendChild(this._recorder.document.createElement(\"x-div\"));\n acceptButton.addEventListener(\"click\", () => {\n var _a;\n return (_a = options.onCommit) == null ? void 0 : _a.call(options);\n });\n const cancelButton = this._recorder.document.createElement(\"x-pw-tool-item\");\n cancelButton.title = \"Close\";\n cancelButton.classList.add(\"cancel\");\n cancelButton.appendChild(this._recorder.document.createElement(\"x-div\"));\n cancelButton.addEventListener(\"click\", () => {\n var _a;\n this.close();\n (_a = options.onCancel) == null ? void 0 : _a.call(options);\n });\n this._dialogElement = this._recorder.document.createElement(\"x-pw-dialog\");\n if (options.autosize)\n this._dialogElement.classList.add(\"autosize\");\n this._keyboardListener = (event) => {\n var _a;\n if (event.key === \"Escape\") {\n this.close();\n (_a = options.onCancel) == null ? void 0 : _a.call(options);\n return;\n }\n if (options.onCommit && event.key === \"Enter\" && (event.ctrlKey || event.metaKey)) {\n if (this._dialogElement)\n options.onCommit();\n return;\n }\n };\n this._onGlassPaneClickHandler = (event) => {\n var _a;\n if (this._dialogElement && event.target instanceof Node && this._dialogElement.contains(event.target))\n return;\n this.close();\n (_a = options.onCancel) == null ? void 0 : _a.call(options);\n };\n this._dialogElement.addEventListener(\"click\", (event) => event.stopPropagation());\n const toolbarElement = this._recorder.document.createElement(\"x-pw-tools-list\");\n const labelElement = this._recorder.document.createElement(\"label\");\n labelElement.textContent = options.label;\n toolbarElement.appendChild(labelElement);\n toolbarElement.appendChild(this._recorder.document.createElement(\"x-spacer\"));\n if (options.onCommit)\n toolbarElement.appendChild(acceptButton);\n toolbarElement.appendChild(cancelButton);\n this._dialogElement.appendChild(toolbarElement);\n const bodyElement = this._recorder.document.createElement(\"x-pw-dialog-body\");\n bodyElement.appendChild(options.body);\n this._dialogElement.appendChild(bodyElement);\n toolbarElement.style.cursor = \"move\";\n let dragStartX = 0, dragStartY = 0, dragStartTop = 0, dragStartLeft = 0;\n const onDragMove = (e) => {\n if (!this._dialogElement) return;\n this._dialogElement.style.top = dragStartTop + e.clientY - dragStartY + \"px\";\n this._dialogElement.style.left = dragStartLeft + e.clientX - dragStartX + \"px\";\n };\n const onDragEnd = () => {\n this._recorder.document.removeEventListener(\"mousemove\", onDragMove, true);\n this._recorder.document.removeEventListener(\"mouseup\", onDragEnd, true);\n };\n toolbarElement.addEventListener(\"mousedown\", (e) => {\n if (e.target.closest(\"x-pw-tool-item\")) return;\n dragStartX = e.clientX;\n dragStartY = e.clientY;\n dragStartTop = parseInt(this._dialogElement.style.top) || 0;\n dragStartLeft = parseInt(this._dialogElement.style.left) || 0;\n this._recorder.document.addEventListener(\"mousemove\", onDragMove, true);\n this._recorder.document.addEventListener(\"mouseup\", onDragEnd, true);\n e.stopPropagation();\n e.preventDefault();\n }, false);\n const consumeDialogEvent = (e) => {\n e.stopPropagation();\n };\n this._dialogElement.addEventListener(\"mousedown\", consumeDialogEvent, false);\n this._dialogElement.addEventListener(\"mouseup\", consumeDialogEvent, false);\n this._dialogElement.addEventListener(\"pointerdown\", consumeDialogEvent, false);\n this._dialogElement.addEventListener(\"pointerup\", consumeDialogEvent, false);\n this._dialogElement.addEventListener(\"click\", consumeDialogEvent, false);\n this._dialogElement.addEventListener(\"dblclick\", consumeDialogEvent, false);\n bodyElement.addEventListener(\"click\", consumeDialogEvent, false);\n this._recorder.highlight.appendChild(this._dialogElement);\n this._recorder.highlight.onGlassPaneClick(this._onGlassPaneClickHandler);\n this._recorder.document.addEventListener(\"keydown\", this._keyboardListener, true);\n return this._dialogElement;\n }\n moveTo(top, left) {\n if (!this._dialogElement)\n return;\n this._dialogElement.style.top = top + \"px\";\n this._dialogElement.style.left = left + \"px\";\n }\n close() {\n if (!this._dialogElement)\n return;\n this._dialogElement.remove();\n this._recorder.highlight.offGlassPaneClick(this._onGlassPaneClickHandler);\n this._recorder.document.removeEventListener(\"keydown\", this._keyboardListener);\n this._dialogElement = null;\n }\n};\nfunction deepActiveElement(document2) {\n let activeElement = document2.activeElement;\n while (activeElement && activeElement.shadowRoot && activeElement.shadowRoot.activeElement)\n activeElement = activeElement.shadowRoot.activeElement;\n return activeElement;\n}\nfunction modifiersForEvent(event) {\n return (event.altKey ? 1 : 0) | (event.ctrlKey ? 2 : 0) | (event.metaKey ? 4 : 0) | (event.shiftKey ? 8 : 0);\n}\nfunction buttonForEvent(event) {\n switch (event.which) {\n case 1:\n return \"left\";\n case 2:\n return \"middle\";\n case 3:\n return \"right\";\n }\n return \"left\";\n}\nfunction positionForEvent(event) {\n const targetElement = event.target;\n if (targetElement.nodeName !== \"CANVAS\")\n return;\n return {\n x: event.offsetX,\n y: event.offsetY\n };\n}\nfunction consumeEvent5(e) {\n e.preventDefault();\n e.stopPropagation();\n e.stopImmediatePropagation();\n}\nfunction asCheckbox(node) {\n if (!node || node.nodeName !== \"INPUT\")\n return null;\n const inputElement = node;\n return [\"checkbox\", \"radio\"].includes(inputElement.type) ? inputElement : null;\n}\nfunction isRangeInput(node) {\n if (!node || node.nodeName !== \"INPUT\")\n return false;\n const inputElement = node;\n return inputElement.type.toLowerCase() === \"range\";\n}\nfunction addEventListener5(target, eventName, listener, useCapture) {\n target.addEventListener(eventName, listener, useCapture);\n const remove = () => {\n target.removeEventListener(eventName, listener, useCapture);\n };\n return remove;\n}\nfunction removeEventListeners3(listeners) {\n for (const listener of listeners)\n listener();\n listeners.splice(0, listeners.length);\n}\nfunction entriesForSelectorHighlight(injectedScript, language, selector, ownerDocument) {\n try {\n const parsedSelector = injectedScript.parseSelector(selector);\n const elements = injectedScript.querySelectorAll(parsedSelector, ownerDocument);\n const color = elements.length > 1 ? HighlightColors2.multiple : HighlightColors2.single;\n const locator = injectedScript.utils.asLocator(language, selector);\n return elements.map((element, index) => {\n const suffix = elements.length > 1 ? ` [${index + 1} of ${elements.length}]` : \"\";\n return { element, color, tooltipText: locator + suffix };\n });\n } catch (e) {\n return [];\n }\n}\nfunction createSvgElement(doc, { tagName, attrs, children }) {\n const elem = doc.createElementNS(\"http://www.w3.org/2000/svg\", tagName);\n if (attrs) {\n for (const [k, v] of Object.entries(attrs))\n elem.setAttribute(k, v);\n }\n if (children) {\n for (const c of children)\n elem.appendChild(createSvgElement(doc, c));\n }\n return elem;\n}\nfunction isAssertAction(action) {\n return action.name.startsWith(\"assert\");\n}\nfunction isLikelyMenuTrigger(el) {\n const tag = el.tagName;\n if (tag === \"BUTTON\" || tag === \"A\")\n return true;\n if (el.hasAttribute(\"aria-haspopup\") || el.hasAttribute(\"aria-expanded\"))\n return true;\n const role = el.getAttribute(\"role\");\n if (role && (role === \"button\" || role === \"link\" || role === \"menuitem\" || role === \"tab\"))\n return true;\n return false;\n}\nfunction closestMenuTrigger(start, document2) {\n let current = start;\n while (current && current !== document2.body && current !== document2.documentElement) {\n if (isLikelyMenuTrigger(current))\n return current;\n current = current.parentElement;\n }\n return null;\n}\nfunction getTimestamp10(recorder) {\n return recorder.injectedScript.utils.builtins.Date.now().toString();\n}\n\n// packages/injected/src/recorder/pollingRecorder.ts\nvar PollingRecorder = class {\n constructor(injectedScript, options) {\n this._recorder = new Recorder(injectedScript, options);\n this._embedder = injectedScript.window;\n injectedScript.onGlobalListenersRemoved.add(() => this._recorder.installListeners());\n const refreshOverlay = () => {\n this._lastStateJSON = void 0;\n this._pollRecorderMode().catch((e) => console.log(e));\n };\n this._embedder.__pw_refreshOverlay = refreshOverlay;\n injectedScript.window.__pw_recorderGenerateSelector = (element, options2) => {\n return injectedScript.generateSelector(element, {\n testIdAttributeName: (options2 == null ? void 0 : options2.testIdAttributeName) || \"data-testid\"\n });\n };\n injectedScript.window.__pw_computeScopedSelector = (element, options2) => {\n return computeScopedSelector(injectedScript, element, (options2 == null ? void 0 : options2.testIdAttributeName) || \"data-testid\");\n };\n refreshOverlay();\n }\n async _pollRecorderMode() {\n const pollPeriod = 1e3;\n if (this._pollRecorderModeTimer)\n this._recorder.injectedScript.utils.builtins.clearTimeout(this._pollRecorderModeTimer);\n const state = await this._embedder.__pw_recorderState().catch(() => null);\n if (!state) {\n this._pollRecorderModeTimer = this._recorder.injectedScript.utils.builtins.setTimeout(() => this._pollRecorderMode(), pollPeriod);\n return;\n }\n const stringifiedState = JSON.stringify(state);\n if (this._lastStateJSON !== stringifiedState) {\n this._lastStateJSON = stringifiedState;\n const win = this._recorder.document.defaultView;\n if (win.top !== win) {\n state.actionPoint = void 0;\n }\n this._recorder.setUIState(state, this);\n }\n this._pollRecorderModeTimer = this._recorder.injectedScript.utils.builtins.setTimeout(() => this._pollRecorderMode(), pollPeriod);\n }\n async performAction(action) {\n await this._embedder.__pw_recorderPerformAction(action);\n }\n async recordAction(action) {\n await this._embedder.__pw_recorderRecordAction(action);\n }\n async elementPicked(elementInfo) {\n await this._embedder.__pw_recorderElementPicked(elementInfo);\n }\n async setMode(mode) {\n await this._embedder.__pw_recorderSetMode(mode);\n }\n async setOverlayState(state) {\n await this._embedder.__pw_recorderSetOverlayState(state);\n }\n};\nvar pollingRecorder_default = PollingRecorder;\n";
1
+ export const source = "\nvar __commonJS = obj => {\n let required = false;\n let result;\n return function __require() {\n if (!required) {\n required = true;\n let fn;\n for (const name in obj) { fn = obj[name]; break; }\n const module = { exports: {} };\n fn(module.exports, module);\n result = module.exports;\n }\n return result;\n }\n};\nvar __export = (target, all) => {for (var name in all) target[name] = all[name];};\nvar __toESM = mod => ({ ...mod, 'default': mod });\nvar __toCommonJS = mod => ({ ...mod, __esModule: true });\n\n\n// packages/injected/src/recorder/pollingRecorder.ts\nvar pollingRecorder_exports = {};\n__export(pollingRecorder_exports, {\n PollingRecorder: () => PollingRecorder,\n default: () => pollingRecorder_default\n});\nmodule.exports = __toCommonJS(pollingRecorder_exports);\n\n// packages/injected/src/recorder/clipPaths.ts\nvar svgJson = { \"tagName\": \"svg\", \"children\": [{ \"tagName\": \"defs\", \"children\": [{ \"tagName\": \"clipPath\", \"attrs\": { \"width\": \"16\", \"height\": \"16\", \"viewBox\": \"0 0 16 16\", \"fill\": \"currentColor\", \"id\": \"icon-gripper\" }, \"children\": [{ \"tagName\": \"path\", \"attrs\": { \"d\": \"M5 3h2v2H5zm0 4h2v2H5zm0 4h2v2H5zm4-8h2v2H9zm0 4h2v2H9zm0 4h2v2H9z\" } }] }, { \"tagName\": \"clipPath\", \"attrs\": { \"width\": \"16\", \"height\": \"16\", \"viewBox\": \"0 0 16 16\", \"fill\": \"currentColor\", \"id\": \"icon-circle-large-filled\" }, \"children\": [{ \"tagName\": \"path\", \"attrs\": { \"d\": \"M8 1a6.8 6.8 0 0 1 1.86.253 6.899 6.899 0 0 1 3.083 1.805 6.903 6.903 0 0 1 1.804 3.083C14.916 6.738 15 7.357 15 8s-.084 1.262-.253 1.86a6.9 6.9 0 0 1-.704 1.674 7.157 7.157 0 0 1-2.516 2.509 6.966 6.966 0 0 1-1.668.71A6.984 6.984 0 0 1 8 15a6.984 6.984 0 0 1-1.86-.246 7.098 7.098 0 0 1-1.674-.711 7.3 7.3 0 0 1-1.415-1.094 7.295 7.295 0 0 1-1.094-1.415 7.098 7.098 0 0 1-.71-1.675A6.985 6.985 0 0 1 1 8c0-.643.082-1.262.246-1.86a6.968 6.968 0 0 1 .711-1.667 7.156 7.156 0 0 1 2.509-2.516 6.895 6.895 0 0 1 1.675-.704A6.808 6.808 0 0 1 8 1z\" } }] }, { \"tagName\": \"clipPath\", \"attrs\": { \"width\": \"16\", \"height\": \"16\", \"viewBox\": \"0 0 16 16\", \"fill\": \"currentColor\", \"id\": \"icon-stop-circle\" }, \"children\": [{ \"tagName\": \"path\", \"attrs\": { \"d\": \"M6 6h4v4H6z\" } }, { \"tagName\": \"path\", \"attrs\": { \"fill-rule\": \"evenodd\", \"clip-rule\": \"evenodd\", \"d\": \"M8.6 1c1.6.1 3.1.9 4.2 2 1.3 1.4 2 3.1 2 5.1 0 1.6-.6 3.1-1.6 4.4-1 1.2-2.4 2.1-4 2.4-1.6.3-3.2.1-4.6-.7-1.4-.8-2.5-2-3.1-3.5C.9 9.2.8 7.5 1.3 6c.5-1.6 1.4-2.9 2.8-3.8C5.4 1.3 7 .9 8.6 1zm.5 12.9c1.3-.3 2.5-1 3.4-2.1.8-1.1 1.3-2.4 1.2-3.8 0-1.6-.6-3.2-1.7-4.3-1-1-2.2-1.6-3.6-1.7-1.3-.1-2.7.2-3.8 1-1.1.8-1.9 1.9-2.3 3.3-.4 1.3-.4 2.7.2 4 .6 1.3 1.5 2.3 2.7 3 1.2.7 2.6.9 3.9.6z\" } }] }, { \"tagName\": \"clipPath\", \"attrs\": { \"width\": \"16\", \"height\": \"16\", \"viewBox\": \"0 0 16 16\", \"fill\": \"currentColor\", \"id\": \"icon-inspect\" }, \"children\": [{ \"tagName\": \"path\", \"attrs\": { \"fill-rule\": \"evenodd\", \"clip-rule\": \"evenodd\", \"d\": \"M1 3l1-1h12l1 1v6h-1V3H2v8h5v1H2l-1-1V3zm14.707 9.707L9 6v9.414l2.707-2.707h4zM10 13V8.414l3.293 3.293h-2L10 13z\" } }] }, { \"tagName\": \"clipPath\", \"attrs\": { \"width\": \"16\", \"height\": \"16\", \"viewBox\": \"0 0 16 16\", \"fill\": \"currentColor\", \"id\": \"icon-whole-word\" }, \"children\": [{ \"tagName\": \"path\", \"attrs\": { \"fill-rule\": \"evenodd\", \"clip-rule\": \"evenodd\", \"d\": \"M0 11H1V13H15V11H16V14H15H1H0V11Z\" } }, { \"tagName\": \"path\", \"attrs\": { \"d\": \"M6.84048 11H5.95963V10.1406H5.93814C5.555 10.7995 4.99104 11.1289 4.24625 11.1289C3.69839 11.1289 3.26871 10.9839 2.95718 10.6938C2.64924 10.4038 2.49527 10.0189 2.49527 9.53906C2.49527 8.51139 3.10041 7.91341 4.3107 7.74512L5.95963 7.51416C5.95963 6.57959 5.58186 6.1123 4.82632 6.1123C4.16389 6.1123 3.56591 6.33789 3.03238 6.78906V5.88672C3.57307 5.54297 4.19612 5.37109 4.90152 5.37109C6.19416 5.37109 6.84048 6.05501 6.84048 7.42285V11ZM5.95963 8.21777L4.63297 8.40039C4.22476 8.45768 3.91682 8.55973 3.70914 8.70654C3.50145 8.84977 3.39761 9.10579 3.39761 9.47461C3.39761 9.74316 3.4925 9.96338 3.68228 10.1353C3.87564 10.3035 4.13166 10.3877 4.45035 10.3877C4.8872 10.3877 5.24706 10.2355 5.52994 9.93115C5.8164 9.62321 5.95963 9.2347 5.95963 8.76562V8.21777Z\" } }, { \"tagName\": \"path\", \"attrs\": { \"d\": \"M9.3475 10.2051H9.32601V11H8.44515V2.85742H9.32601V6.4668H9.3475C9.78076 5.73633 10.4146 5.37109 11.2489 5.37109C11.9543 5.37109 12.5057 5.61816 12.9032 6.1123C13.3042 6.60286 13.5047 7.26172 13.5047 8.08887C13.5047 9.00911 13.2809 9.74674 12.8333 10.3018C12.3857 10.8532 11.7734 11.1289 10.9964 11.1289C10.2695 11.1289 9.71989 10.821 9.3475 10.2051ZM9.32601 7.98682V8.75488C9.32601 9.20964 9.47282 9.59635 9.76644 9.91504C10.0636 10.2301 10.4396 10.3877 10.8944 10.3877C11.4279 10.3877 11.8451 10.1836 12.1458 9.77539C12.4502 9.36719 12.6024 8.79964 12.6024 8.07275C12.6024 7.46045 12.4609 6.98063 12.1781 6.6333C11.8952 6.28597 11.512 6.1123 11.0286 6.1123C10.5166 6.1123 10.1048 6.29134 9.7933 6.64941C9.48177 7.00391 9.32601 7.44971 9.32601 7.98682Z\" } }] }, { \"tagName\": \"clipPath\", \"attrs\": { \"width\": \"16\", \"height\": \"16\", \"viewBox\": \"0 0 16 16\", \"fill\": \"currentColor\", \"id\": \"icon-eye\" }, \"children\": [{ \"tagName\": \"path\", \"attrs\": { \"d\": \"M7.99993 6.00316C9.47266 6.00316 10.6666 7.19708 10.6666 8.66981C10.6666 10.1426 9.47266 11.3365 7.99993 11.3365C6.52715 11.3365 5.33324 10.1426 5.33324 8.66981C5.33324 7.19708 6.52715 6.00316 7.99993 6.00316ZM7.99993 7.00315C7.07946 7.00315 6.33324 7.74935 6.33324 8.66981C6.33324 9.59028 7.07946 10.3365 7.99993 10.3365C8.9204 10.3365 9.6666 9.59028 9.6666 8.66981C9.6666 7.74935 8.9204 7.00315 7.99993 7.00315ZM7.99993 3.66675C11.0756 3.66675 13.7307 5.76675 14.4673 8.70968C14.5344 8.97755 14.3716 9.24908 14.1037 9.31615C13.8358 9.38315 13.5643 9.22041 13.4973 8.95248C12.8713 6.45205 10.6141 4.66675 7.99993 4.66675C5.38454 4.66675 3.12664 6.45359 2.50182 8.95555C2.43491 9.22341 2.16348 9.38635 1.89557 9.31948C1.62766 9.25255 1.46471 8.98115 1.53162 8.71321C2.26701 5.76856 4.9229 3.66675 7.99993 3.66675Z\" } }] }, { \"tagName\": \"clipPath\", \"attrs\": { \"width\": \"16\", \"height\": \"16\", \"viewBox\": \"0 0 16 16\", \"fill\": \"currentColor\", \"id\": \"icon-symbol-constant\" }, \"children\": [{ \"tagName\": \"path\", \"attrs\": { \"fill-rule\": \"evenodd\", \"clip-rule\": \"evenodd\", \"d\": \"M4 6h8v1H4V6zm8 3H4v1h8V9z\" } }, { \"tagName\": \"path\", \"attrs\": { \"fill-rule\": \"evenodd\", \"clip-rule\": \"evenodd\", \"d\": \"M1 4l1-1h12l1 1v8l-1 1H2l-1-1V4zm1 0v8h12V4H2z\" } }] }, { \"tagName\": \"clipPath\", \"attrs\": { \"width\": \"16\", \"height\": \"16\", \"viewBox\": \"0 0 16 16\", \"fill\": \"currentColor\", \"id\": \"icon-check\" }, \"children\": [{ \"tagName\": \"path\", \"attrs\": { \"fill-rule\": \"evenodd\", \"clip-rule\": \"evenodd\", \"d\": \"M14.431 3.323l-8.47 10-.79-.036-3.35-4.77.818-.574 2.978 4.24 8.051-9.506.764.646z\" } }] }, { \"tagName\": \"clipPath\", \"attrs\": { \"width\": \"16\", \"height\": \"16\", \"viewBox\": \"0 0 16 16\", \"fill\": \"none\", \"stroke\": \"currentColor\", \"stroke-linecap\": \"round\", \"stroke-linejoin\": \"round\", \"stroke-width\": \"1\", \"id\": \"icon-location-pin\" }, \"children\": [{ \"tagName\": \"path\", \"attrs\": { \"d\": \"\\n M8 1\\n C5.243 1 3 3.243 3 6\\n C3 9 8 14 8 14\\n C8 14 13 9 13 6\\n C13 3.243 10.757 1 8 1\\n Z\\n M6 6\\n A2 2 0 1 1 10 6\\n A2 2 0 1 1 6 6\\n Z\\n \" } }] }, { \"tagName\": \"clipPath\", \"attrs\": { \"width\": \"16\", \"height\": \"16\", \"viewBox\": \"0 0 16 16\", \"fill\": \"currentColor\", \"id\": \"icon-layers\" }, \"children\": [{ \"tagName\": \"path\", \"attrs\": { \"d\": \"M8 2L2 5v1l6 3 6-3V5L8 2zm0 1.18L11.82 5 8 6.82 4.18 5 8 3.18zM2 7.13V8l6 3 6-3v-.87L8 10.2 2 7.13zM2 10.13V11l6 3 6-3v-.87L8 13.2 2 10.13z\" } }] }, { \"tagName\": \"clipPath\", \"attrs\": { \"width\": \"16\", \"height\": \"16\", \"viewBox\": \"0 0 16 16\", \"fill\": \"currentColor\", \"id\": \"icon-list-tree\" }, \"children\": [{ \"tagName\": \"path\", \"attrs\": { \"d\": \"M2 3.5C2 3.22386 2.22386 3 2.5 3H13.5C13.7761 3 14 3.22386 14 3.5C14 3.77614 13.7761 4 13.5 4H6V6H13.5C13.7761 6 14 6.22386 14 6.5C14 6.77614 13.7761 7 13.5 7H6V9H13.5C13.7761 9 14 9.22386 14 9.5C14 9.77614 13.7761 10 13.5 10H6V12H13.5C13.7761 12 14 12.2239 14 12.5C14 12.7761 13.7761 13 13.5 13H5.5C5.22386 13 5 12.7761 5 12.5V4H2.5C2.22386 4 2 3.77614 2 3.5Z\" } }] }, { \"tagName\": \"clipPath\", \"attrs\": { \"width\": \"16\", \"height\": \"16\", \"viewBox\": \"0 0 16 16\", \"fill\": \"currentColor\", \"id\": \"icon-brackets\" }, \"children\": [{ \"tagName\": \"path\", \"attrs\": { \"d\": \"M4.5 2H2v12h2.5v-1H3V3h1.5V2zm7 0H14v12h-2.5v-1H13V3h-1.5V2zM6 5h4v1H6V5zm0 3h4v1H6V8zm0 3h4v1H6v-1z\" } }] }, { \"tagName\": \"clipPath\", \"attrs\": { \"width\": \"16\", \"height\": \"16\", \"viewBox\": \"0 0 16 16\", \"fill\": \"currentColor\", \"id\": \"icon-braces\" }, \"children\": [{ \"tagName\": \"path\", \"attrs\": { \"fill-rule\": \"evenodd\", \"clip-rule\": \"evenodd\", \"d\": \"M3 3.5C3 2.67 3.67 2 4.5 2H5v1h-.5c-.28 0-.5.22-.5.5v3c0 .83-.67 1.5-1.5 1.5.83 0 1.5.67 1.5 1.5v3c0 .28.22.5.5.5H5v1h-.5C3.67 14 3 13.33 3 12.5v-3c0-.28-.22-.5-.5-.5H2V8h.5c.28 0 .5-.22.5-.5v-3zm10 0C13 2.67 12.33 2 11.5 2H11v1h.5c.28 0 .5.22.5.5v3c0 .83.67 1.5 1.5 1.5-.83 0-1.5.67-1.5 1.5v3c0 .28-.22.5-.5.5H11v1h.5c.83 0 1.5-.67 1.5-1.5v-3c0-.28.22-.5.5-.5H14V8h-.5c-.28 0-.5-.22-.5-.5v-3z\" } }] }, { \"tagName\": \"clipPath\", \"attrs\": { \"width\": \"16\", \"height\": \"16\", \"viewBox\": \"0 0 16 16\", \"fill\": \"currentColor\", \"id\": \"icon-braces-dashes\" }, \"children\": [{ \"tagName\": \"path\", \"attrs\": { \"d\": \"M3 3.5C3 2.67 3.67 2 4.5 2H5v1h-.5c-.28 0-.5.22-.5.5v3c0 .83-.67 1.5-1.5 1.5.83 0 1.5.67 1.5 1.5v3c0 .28.22.5.5.5H5v1h-.5C3.67 14 3 13.33 3 12.5v-3c0-.28-.22-.5-.5-.5H2V8h.5c.28 0 .5-.22.5-.5v-3zm10 0C13 2.67 12.33 2 11.5 2H11v1h.5c.28 0 .5.22.5.5v3c0 .83.67 1.5 1.5 1.5-.83 0-1.5.67-1.5 1.5v3c0 .28-.22.5-.5.5H11v1h.5c.83 0 1.5-.67 1.5-1.5v-3c0-.28.22-.5.5-.5H14V8h-.5c-.28 0-.5-.22-.5-.5v-3zM6 5h4v1H6V5zm0 3h4v1H6V8zm0 3h4v1H6v-1z\" } }] }, { \"tagName\": \"clipPath\", \"attrs\": { \"width\": \"16\", \"height\": \"16\", \"viewBox\": \"0 0 16 16\", \"fill\": \"currentColor\", \"id\": \"icon-close\" }, \"children\": [{ \"tagName\": \"path\", \"attrs\": { \"fill-rule\": \"evenodd\", \"clip-rule\": \"evenodd\", \"d\": \"M8 8.707l3.646 3.647.708-.707L8.707 8l3.647-3.646-.707-.708L8 7.293 4.354 3.646l-.707.708L7.293 8l-3.646 3.646.707.708L8 8.707z\" } }] }, { \"tagName\": \"clipPath\", \"attrs\": { \"width\": \"16\", \"height\": \"16\", \"viewBox\": \"0 0 16 16\", \"fill\": \"currentColor\", \"id\": \"icon-pass\" }, \"children\": [{ \"tagName\": \"path\", \"attrs\": { \"d\": \"M6.27 10.87h.71l4.56-4.56-.71-.71-4.2 4.21-1.92-1.92L4 8.6l2.27 2.27z\" } }, { \"tagName\": \"path\", \"attrs\": { \"fill-rule\": \"evenodd\", \"clip-rule\": \"evenodd\", \"d\": \"M8.6 1c1.6.1 3.1.9 4.2 2 1.3 1.4 2 3.1 2 5.1 0 1.6-.6 3.1-1.6 4.4-1 1.2-2.4 2.1-4 2.4-1.6.3-3.2.1-4.6-.7-1.4-.8-2.5-2-3.1-3.5C.9 9.2.8 7.5 1.3 6c.5-1.6 1.4-2.9 2.8-3.8C5.4 1.3 7 .9 8.6 1zm.5 12.9c1.3-.3 2.5-1 3.4-2.1.8-1.1 1.3-2.4 1.2-3.8 0-1.6-.6-3.2-1.7-4.3-1-1-2.2-1.6-3.6-1.7-1.3-.1-2.7.2-3.8 1-1.1.8-1.9 1.9-2.3 3.3-.4 1.3-.4 2.7.2 4 .6 1.3 1.5 2.3 2.7 3 1.2.7 2.6.9 3.9.6z\" } }] }, { \"tagName\": \"clipPath\", \"attrs\": { \"width\": \"16\", \"height\": \"16\", \"viewBox\": \"0 0 16 16\", \"fill\": \"currentColor\", \"id\": \"icon-gist\" }, \"children\": [{ \"tagName\": \"path\", \"attrs\": { \"fill-rule\": \"evenodd\", \"clip-rule\": \"evenodd\", \"d\": \"M10.57 1.14l3.28 3.3.15.36v9.7l-.5.5h-11l-.5-.5v-13l.5-.5h7.72l.35.14zM10 5h3l-3-3v3zM3 2v12h10V6H9.5L9 5.5V2H3zm2.062 7.533l1.817-1.828L6.17 7 4 9.179v.707l2.171 2.174.707-.707-1.816-1.82zM8.8 7.714l.7-.709 2.189 2.175v.709L9.5 12.062l-.705-.709 1.831-1.82L8.8 7.714z\" } }] }, { \"tagName\": \"clipPath\", \"attrs\": { \"width\": \"16\", \"height\": \"16\", \"viewBox\": \"0 0 16 16\", \"fill\": \"currentColor\", \"id\": \"icon-snapshot\" }, \"children\": [{ \"tagName\": \"path\", \"attrs\": { \"d\": \"M2 1.5l.5-.5h7.72l.35.14 3.28 3.3.15.36v9.7l-.5.5h-11l-.5-.5v-13zm1 .5v12h10V6H9.5L9 5.5V2H3zm7 0v3h3l-3-3z\" } }, { \"tagName\": \"path\", \"attrs\": { \"fill\": \"none\", \"stroke\": \"currentColor\", \"stroke-width\": \"0.8\", \"transform\": \"rotate(7 8 8.7)\", \"d\": \"M 10 5.2 C 8 5.2, 6 5.7, 6 7.2 C 6 8.7, 7.5 8.7, 8 8.7 C 8.5 8.7, 10 8.7, 10 10.2 C 10 11.7, 8 12.2, 6 12.2\" } }] }, { \"tagName\": \"clipPath\", \"attrs\": { \"width\": \"16\", \"height\": \"16\", \"viewBox\": \"0 0 16 16\", \"fill\": \"currentColor\", \"id\": \"icon-move\" }, \"children\": [{ \"tagName\": \"path\", \"attrs\": { \"fill-rule\": \"evenodd\", \"clip-rule\": \"evenodd\", \"d\": \"M8.5 2.5V5.5H7.5V2.5L6.20711 3.79289L5.5 3.08579L8 0.585786L10.5 3.08579L9.79289 3.79289L8.5 2.5ZM7.5 10.5V13.5L6.20711 12.2071L5.5 12.9142L8 15.4142L10.5 12.9142L9.79289 12.2071L8.5 13.5V10.5H7.5ZM10.5 8.5H13.5L12.2071 9.79289L12.9142 10.5L15.4142 8L12.9142 5.5L12.2071 6.20711L13.5 7.5H10.5V8.5ZM5.5 7.5H2.5L3.79289 6.20711L3.08579 5.5L0.585786 8L3.08579 10.5L3.79289 9.79289L2.5 8.5H5.5V7.5Z\" } }] }, { \"tagName\": \"clipPath\", \"attrs\": { \"width\": \"16\", \"height\": \"16\", \"viewBox\": \"0 0 16 16\", \"fill\": \"currentColor\", \"id\": \"icon-selection\" }, \"children\": [{ \"tagName\": \"path\", \"attrs\": { \"fill-rule\": \"evenodd\", \"clip-rule\": \"evenodd\", \"d\": \"M1 1H3V2H2V3H1V1ZM4 1H6V2H4V1ZM7 1H9V2H7V1ZM10 1H12V2H10V1ZM13 1H15V3H14V2H13V1ZM14 4H15V6H14V4ZM14 7H15V9H14V7ZM14 10H15V12H14V10ZM13 13V14H14V15H13H12V14H13V13H14V12H15V13V15H13ZM10 14H12V15H10V14ZM7 14H9V15H7V14ZM4 14H6V15H4V14ZM1 13H2V14H3V15H1V13ZM1 10H2V12H1V10ZM1 7H2V9H1V7ZM1 4H2V6H1V4Z\" } }] }, { \"tagName\": \"clipPath\", \"attrs\": { \"viewBox\": \"0 0 16 16\", \"fill\": \"currentColor\", \"id\": \"icon-table\" }, \"children\": [{ \"tagName\": \"path\", \"attrs\": { \"fill-rule\": \"evenodd\", \"d\": \"M2 3h12v10H2V3zm1 1v8h10V4H3z\" } }, { \"tagName\": \"rect\", \"attrs\": { \"x\": \"2\", \"y\": \"6.33\", \"width\": \"12\", \"height\": \"1\" } }, { \"tagName\": \"rect\", \"attrs\": { \"x\": \"2\", \"y\": \"9.67\", \"width\": \"12\", \"height\": \"1\" } }, { \"tagName\": \"rect\", \"attrs\": { \"x\": \"5.67\", \"y\": \"3\", \"width\": \"1\", \"height\": \"10\" } }, { \"tagName\": \"rect\", \"attrs\": { \"x\": \"9.33\", \"y\": \"3\", \"width\": \"1\", \"height\": \"10\" } }] }, { \"tagName\": \"clipPath\", \"attrs\": { \"viewBox\": \"0 0 16 16\", \"fill\": \"currentColor\", \"id\": \"icon-file-upload\" }, \"children\": [{ \"tagName\": \"path\", \"attrs\": { \"d\": \"M14.5 3H7.71l-.85-.85L6.51 2h-5l-.5.5v11l.5.5h13l.5-.5v-10L14.5 3zm-.51 8.49V13h-12V7h4.49l.35-.15.86-.86H14v1.5l.001 4zm0-6.49h-6.5l-.35.15-.86.86H2v-3h4.29l.85.85.36.15H14l-.01.99z\" } }] }, { \"tagName\": \"clipPath\", \"attrs\": { \"viewBox\": \"0 0 16 16\", \"fill\": \"currentColor\", \"id\": \"icon-eraser\" }, \"children\": [{ \"tagName\": \"path\", \"attrs\": { \"d\": \"M 13.54 2.70 L 11.30 0.46 C 10.68 -0.15 9.68 -0.15 9.06 0.46 L 3.34 6.18 L 1.46 8.07 C 0.85 8.68 0.85 9.68 1.46 10.30 L 3.70 12.54 C 4.01 12.84 4.41 13 4.82 13 C 5.22 13 5.63 12.84 5.93 12.54 L 7.82 10.66 L 8.26 10.21 L 13.54 4.93 C 13.84 4.64 14 4.24 14 3.82 C 14 3.39 13.84 3.00 13.54 2.70 Z M 7.37 10.21 L 5.49 12.09 C 5.12 12.46 4.51 12.46 4.15 12.09 L 1.91 9.85 C 1.54 9.48 1.54 8.88 1.91 8.51 L 3.79 6.63 L 7.37 10.21 Z M 13.09 4.49 L 7.82 9.76 L 6.03 7.97 L 4.24 6.18 L 9.51 0.91 C 9.88 0.54 10.48 0.54 10.85 0.91 L 13.09 3.15 C 13.27 3.33 13.37 3.56 13.37 3.82 C 13.37 4.07 13.27 4.31 13.09 4.49 Z\" } }, { \"tagName\": \"rect\", \"attrs\": { \"x\": \"1\", \"y\": \"13.2\", \"width\": \"12\", \"height\": \"1.6\" } }] }, { \"tagName\": \"clipPath\", \"attrs\": { \"viewBox\": \"0 0 100 100\", \"fill\": \"currentColor\", \"id\": \"icon-sketch-tool\" }, \"children\": [{ \"tagName\": \"path\", \"attrs\": { \"d\": \"m37.68 70.594c-0.36328 0.75781-0.66406 1.5508-0.89063 2.3594-0.41406 1.4727-0.58984 3.0195-0.51562 4.5742 0.042968 0.95312-0.58984 1.7852-1.4766 2.0195l-21.281 5.707c-1.0664 0.28516-2.1602-0.34766-2.4453-1.4141-0.09375-0.35156-0.085937-0.70703 0-1.0312l5.7031-21.285c0.25781-0.96094 1.1719-1.5703 2.1289-1.4727 1.5195 0.0625 3.0273-0.11719 4.4688-0.51953 0.80859-0.22656 1.5977-0.52734 2.3594-0.89453-0.64453-0.78516-0.60156-1.9453 0.13281-2.6797l3.7227-3.7227c0.70703-0.70703 1.8086-0.77344 2.5898-0.20312l35.953-35.953c-0.57031-0.78125-0.50391-1.8828 0.20312-2.5898l3.7227-3.7227c0.70703-0.70703 1.8086-0.77344 2.5898-0.20312l6.5039-6.5039c0.78125-0.78125 2.0469-0.78125 2.8281 0l9.2891 9.2891c0.78125 0.78125 0.78125 2.0469 0 2.8281l-6.5039 6.5039c0.57031 0.78125 0.5 1.8828-0.20312 2.5859l-0.49219 0.49219 2.875 2.875c0.78125 0.78125 0.78125 2.0469 0 2.8281l-16.711 16.711c-0.78125 0.78125-2.0469 0.78125-2.8281 0s-0.78125-2.0469 0-2.8281l15.297-15.297-1.4609-1.4609-0.40234 0.40234c-0.70703 0.70703-1.8047 0.77344-2.5859 0.20312l-35.953 35.953c0.56641 0.78125 0.5 1.8828-0.20312 2.5859l-3.7227 3.7227c-0.73438 0.73438-1.8945 0.77734-2.6797 0.13281zm-9.0078-8.9961c-1.3281 0.76562-2.75 1.3594-4.2266 1.7734-1.3711 0.37891-2.7812 0.60547-4.2109 0.66406l-3.375 12.598 5.2109-5.2109c-0.18359-0.51562-0.27734-1.0547-0.27734-1.5938 0-1.1992 0.46094-2.4023 1.3789-3.3203 0.91406-0.91406 2.1211-1.375 3.3203-1.375 1.1953 0 2.3945 0.46094 3.3125 1.375 0.92187 0.92578 1.3828 2.1289 1.3828 3.3203 0 1.1406-0.41797 2.2852-1.25 3.1836-0.88672 0.96875-2.1328 1.5117-3.4453 1.5117-0.53906 0-1.0742-0.09375-1.5898-0.28125l-5.2148 5.2148 12.598-3.375c0.058594-1.4336 0.28516-2.8477 0.66406-4.2109 0.41406-1.4766 1.0078-2.8984 1.7734-4.2266l-6.0508-6.0508zm11.168 3.7227-8.8438-8.8438-0.89453 0.89453 8.8438 8.8438zm37.645-52.945 6.4609 6.4609 5.0742-5.0742-6.4609-6.4609zm-0.089844 13.012-6.4609-6.4609-35.871 35.871 6.4609 6.4609zm4.6367-2.8086-8.293-8.293c-0.12109-0.054687-0.23828-0.125-0.34766-0.20312l-0.82422 0.82422 8.8438 8.8438 0.82031-0.82031c-0.082031-0.10938-0.14844-0.23047-0.20312-0.35156zm-75.164 68.324c-0.83984-0.71094-0.94531-1.9727-0.23438-2.8125 0.71094-0.83984 1.9727-0.94531 2.8125-0.23438 11.77 10.004 24.934 4.5469 38.125-0.91797 14.867-6.1602 29.77-12.34 43.941 0.98828 0.80078 0.75391 0.83984 2.0195 0.085937 2.8203-0.75391 0.80078-2.0195 0.83984-2.8203 0.085937-12.289-11.559-26-5.875-39.676-0.20703-14.32 5.9336-28.609 11.855-42.234 0.27734zm19.625-20.375c0.23437 0 0.37891-0.09375 0.53906-0.24609 0.10156-0.125 0.15625-0.28516 0.15625-0.44922 0-0.18359-0.066406-0.36328-0.19922-0.49609-0.13281-0.13281-0.3125-0.19922-0.49609-0.19922-0.17969 0-0.35938 0.070312-0.49219 0.20312-0.13281 0.13281-0.20312 0.31641-0.20312 0.49219 0 0.17969 0.070313 0.35938 0.20312 0.49219 0.13281 0.13672 0.30078 0.20312 0.49219 0.20312z\" } }] }, { \"tagName\": \"clipPath\", \"attrs\": { \"width\": \"16\", \"height\": \"16\", \"viewBox\": \"0 0 16 16\", \"fill\": \"currentColor\", \"id\": \"icon-gojs-link\" }, \"children\": [{ \"tagName\": \"path\", \"attrs\": { \"d\": \"M 0.75 3.5 A 1.75 1.75 0 1 0 4.25 3.5 A 1.75 1.75 0 1 0 0.75 3.5 Z\" } }, { \"tagName\": \"path\", \"attrs\": { \"d\": \"M 4.25 2.75 L 9.5 2.75 L 9.5 11.75 L 13 11.75 L 13 13.25 L 8 13.25 L 8 4.25 L 4.25 4.25 Z\" } }, { \"tagName\": \"path\", \"attrs\": { \"d\": \"M 13 11 L 15.5 12.5 L 13 14 Z\" } }] }] }] };\nvar clipPaths_default = svgJson;\n\n// packages/playwright-core/src/utils/isomorphic/volatileDate.ts\nvar kMonthNamePattern = \"(?:jan(?:uary)?|feb(?:ruary)?|mar(?:ch)?|apr(?:il)?|may|jun(?:e)?|jul(?:y)?|aug(?:ust)?|sep(?:t(?:ember)?)?|oct(?:ober)?|nov(?:ember)?|dec(?:ember)?)\";\nvar kVolatileDateTokenRe = new RegExp([\n // Numeric date(-time) form. Apps default-name new entities with a creation\n // timestamp (\"Untitled 2026-07-12 17:30:03\"), and row names concatenate\n // further cells after it, so the numeric form also sits MID-name — where\n // the leading/trailing-only strips in hasStrippableDate never match.\n // Deliberately NO leading word boundary: elementText glues adjacent cell\n // texts without a separator (\"data.csv2026/05/18 23:59\"), so the year can\n // start at a letter-digit seam. The required [-/] separators keep\n // version-like number runs (\"release 1.2.3\") from being flagged.\n \"\\\\d{4}[-/]\\\\d{1,2}[-/]\\\\d{1,2}(?:[\\\\sT,]\\\\d{1,2}:\\\\d{1,2}(?::\\\\d{1,2})?)?\",\n `\\\\b${kMonthNamePattern}\\\\b\\\\.?\\\\s+\\\\d{1,2}(?:,?\\\\s*\\\\d{4})?\\\\b`,\n // Day-month form (\"10 Apr\", \"10 Apr 2025\"). The (?!\\s*\\d) guard rejects a\n // false parse where a stable name's trailing digit is read as the day —\n // in \"Marketing 4 Apr 19, 2024\" the \"4 Apr\" is followed by the real day\n // number, so the month-day branch above matches \"Apr 19, 2024\" instead.\n `\\\\b\\\\d{1,2}\\\\s+${kMonthNamePattern}\\\\b\\\\.?(?:\\\\s+\\\\d{4}\\\\b)?(?!\\\\s*\\\\d)`,\n // The (?!['’]) guard keeps possessive/stable labels (\"Today's Deals\",\n // \"Tomorrow's Agenda\") from being flagged — the apostrophe continuation\n // means the word is part of a larger noun phrase, not a date cell.\n `\\\\b(?:today|yesterday|tomorrow|just now)\\\\b(?!['\\u2019])`,\n // \"a few\" covers moment.js/dayjs's default smallest bucket (\"a few seconds ago\").\n \"\\\\b(?:a few|an?|\\\\d+)\\\\s+(?:second|minute|hour|day|week|month|year)s?\\\\s+ago\\\\b\"\n].join(\"|\"), \"i\");\nfunction hasVolatileDateFragment(text) {\n return kVolatileDateTokenRe.test(text);\n}\nvar kVolatileDateTokenStickyRe = new RegExp(kVolatileDateTokenRe.source, \"iy\");\n\n// packages/injected/src/domUtils.ts\nfunction parentElementOrShadowHost(element) {\n if (element.parentElement)\n return element.parentElement;\n if (!element.parentNode)\n return;\n if (element.parentNode.nodeType === 11 && element.parentNode.host)\n return element.parentNode.host;\n}\nfunction enclosingShadowRootOrDocument(element) {\n let node = element;\n while (node.parentNode)\n node = node.parentNode;\n if (node.nodeType === 11 || node.nodeType === 9)\n return node;\n}\nfunction enclosingShadowHost(element) {\n while (element.parentElement)\n element = element.parentElement;\n return parentElementOrShadowHost(element);\n}\nfunction closestCrossShadow(element, css, scope) {\n while (element) {\n const closest = element.closest(css);\n if (scope && closest !== scope && (closest == null ? void 0 : closest.contains(scope)))\n return;\n if (closest)\n return closest;\n element = enclosingShadowHost(element);\n }\n}\nfunction elementSafeTagName(element) {\n const tagName = element.tagName;\n if (typeof tagName === \"string\")\n return tagName.toUpperCase();\n if (element instanceof HTMLFormElement)\n return \"FORM\";\n return element.tagName.toUpperCase();\n}\n\n// packages/injected/src/roleUtils.ts\nfunction hasExplicitAccessibleName(e) {\n return e.hasAttribute(\"aria-label\") || e.hasAttribute(\"aria-labelledby\");\n}\nvar kAncestorPreventingLandmark = \"article:not([role]), aside:not([role]), main:not([role]), nav:not([role]), section:not([role]), [role=article], [role=complementary], [role=main], [role=navigation], [role=region]\";\nvar kGlobalAriaAttributes = [\n [\"aria-atomic\", void 0],\n [\"aria-busy\", void 0],\n [\"aria-controls\", void 0],\n [\"aria-current\", void 0],\n [\"aria-describedby\", void 0],\n [\"aria-details\", void 0],\n // Global use deprecated in ARIA 1.2\n // ['aria-disabled', undefined],\n [\"aria-dropeffect\", void 0],\n // Global use deprecated in ARIA 1.2\n // ['aria-errormessage', undefined],\n [\"aria-flowto\", void 0],\n [\"aria-grabbed\", void 0],\n // Global use deprecated in ARIA 1.2\n // ['aria-haspopup', undefined],\n [\"aria-hidden\", void 0],\n // Global use deprecated in ARIA 1.2\n // ['aria-invalid', undefined],\n [\"aria-keyshortcuts\", void 0],\n [\"aria-label\", [\"caption\", \"code\", \"deletion\", \"emphasis\", \"generic\", \"insertion\", \"paragraph\", \"presentation\", \"strong\", \"subscript\", \"superscript\"]],\n [\"aria-labelledby\", [\"caption\", \"code\", \"deletion\", \"emphasis\", \"generic\", \"insertion\", \"paragraph\", \"presentation\", \"strong\", \"subscript\", \"superscript\"]],\n [\"aria-live\", void 0],\n [\"aria-owns\", void 0],\n [\"aria-relevant\", void 0],\n [\"aria-roledescription\", [\"generic\"]]\n];\nfunction hasGlobalAriaAttribute(element, forRole) {\n return kGlobalAriaAttributes.some(([attr, prohibited]) => {\n return !(prohibited == null ? void 0 : prohibited.includes(forRole || \"\")) && element.hasAttribute(attr);\n });\n}\nfunction hasTabIndex(element) {\n return !Number.isNaN(Number(String(element.getAttribute(\"tabindex\"))));\n}\nfunction isFocusable(element) {\n return !isNativelyDisabled(element) && (isNativelyFocusable(element) || hasTabIndex(element));\n}\nfunction isNativelyFocusable(element) {\n const tagName = elementSafeTagName(element);\n if ([\"BUTTON\", \"DETAILS\", \"SELECT\", \"TEXTAREA\"].includes(tagName))\n return true;\n if (tagName === \"A\" || tagName === \"AREA\")\n return element.hasAttribute(\"href\");\n if (tagName === \"INPUT\")\n return !element.hidden;\n return false;\n}\nvar kImplicitRoleByTagName = {\n \"A\": (e) => {\n return e.hasAttribute(\"href\") ? \"link\" : null;\n },\n \"AREA\": (e) => {\n return e.hasAttribute(\"href\") ? \"link\" : null;\n },\n \"ARTICLE\": () => \"article\",\n \"ASIDE\": () => \"complementary\",\n \"BLOCKQUOTE\": () => \"blockquote\",\n \"BUTTON\": () => \"button\",\n \"CAPTION\": () => \"caption\",\n \"CODE\": () => \"code\",\n \"DATALIST\": () => \"listbox\",\n \"DD\": () => \"definition\",\n \"DEL\": () => \"deletion\",\n \"DETAILS\": () => \"group\",\n \"DFN\": () => \"term\",\n \"DIALOG\": () => \"dialog\",\n \"DT\": () => \"term\",\n \"EM\": () => \"emphasis\",\n \"FIELDSET\": () => \"group\",\n \"FIGURE\": () => \"figure\",\n \"FOOTER\": (e) => closestCrossShadow(e, kAncestorPreventingLandmark) ? null : \"contentinfo\",\n \"FORM\": (e) => hasExplicitAccessibleName(e) ? \"form\" : null,\n \"H1\": () => \"heading\",\n \"H2\": () => \"heading\",\n \"H3\": () => \"heading\",\n \"H4\": () => \"heading\",\n \"H5\": () => \"heading\",\n \"H6\": () => \"heading\",\n \"HEADER\": (e) => closestCrossShadow(e, kAncestorPreventingLandmark) ? null : \"banner\",\n \"HR\": () => \"separator\",\n \"HTML\": () => \"document\",\n \"IMG\": (e) => e.getAttribute(\"alt\") === \"\" && !e.getAttribute(\"title\") && !hasGlobalAriaAttribute(e) && !hasTabIndex(e) ? \"presentation\" : \"img\",\n \"INPUT\": (e) => {\n const type = e.type.toLowerCase();\n if (type === \"search\")\n return e.hasAttribute(\"list\") ? \"combobox\" : \"searchbox\";\n if ([\"email\", \"tel\", \"text\", \"url\", \"\"].includes(type)) {\n const list = getIdRefs(e, e.getAttribute(\"list\"))[0];\n return list && elementSafeTagName(list) === \"DATALIST\" ? \"combobox\" : \"textbox\";\n }\n if (type === \"hidden\")\n return null;\n if (type === \"file\")\n return \"button\";\n return inputTypeToRole[type] || \"textbox\";\n },\n \"INS\": () => \"insertion\",\n \"LI\": () => \"listitem\",\n \"MAIN\": () => \"main\",\n \"MARK\": () => \"mark\",\n \"MATH\": () => \"math\",\n \"MENU\": () => \"list\",\n \"METER\": () => \"meter\",\n \"NAV\": () => \"navigation\",\n \"OL\": () => \"list\",\n \"OPTGROUP\": () => \"group\",\n \"OPTION\": () => \"option\",\n \"OUTPUT\": () => \"status\",\n \"P\": () => \"paragraph\",\n \"PROGRESS\": () => \"progressbar\",\n \"SEARCH\": () => \"search\",\n \"SECTION\": (e) => hasExplicitAccessibleName(e) ? \"region\" : null,\n \"SELECT\": (e) => e.hasAttribute(\"multiple\") || e.size > 1 ? \"listbox\" : \"combobox\",\n \"STRONG\": () => \"strong\",\n \"SUB\": () => \"subscript\",\n \"SUP\": () => \"superscript\",\n // For <svg> we default to Chrome behavior:\n // - Chrome reports 'img'.\n // - Firefox reports 'diagram' that is not in official ARIA spec yet.\n // - Safari reports 'no role', but still computes accessible name.\n \"SVG\": () => \"img\",\n \"TABLE\": () => \"table\",\n \"TBODY\": () => \"rowgroup\",\n \"TD\": (e) => {\n const table = closestCrossShadow(e, \"table\");\n const role = table ? getExplicitAriaRole(table) : \"\";\n return role === \"grid\" || role === \"treegrid\" ? \"gridcell\" : \"cell\";\n },\n \"TEXTAREA\": () => \"textbox\",\n \"TFOOT\": () => \"rowgroup\",\n \"TH\": (e) => {\n const scope = e.getAttribute(\"scope\");\n if (scope === \"col\" || scope === \"colgroup\")\n return \"columnheader\";\n if (scope === \"row\" || scope === \"rowgroup\")\n return \"rowheader\";\n const nextSibling = e.nextElementSibling;\n const prevSibling = e.previousElementSibling;\n const row = !!e.parentElement && elementSafeTagName(e.parentElement) === \"TR\" ? e.parentElement : void 0;\n if (!nextSibling && !prevSibling) {\n if (row) {\n const table = closestCrossShadow(row, \"table\");\n if (table && table.rows.length <= 1)\n return null;\n }\n return \"columnheader\";\n }\n if (isHeaderCell(nextSibling) && isHeaderCell(prevSibling))\n return \"columnheader\";\n if (isNonEmptyDataCell(nextSibling) || isNonEmptyDataCell(prevSibling))\n return \"rowheader\";\n return \"columnheader\";\n },\n \"THEAD\": () => \"rowgroup\",\n \"TIME\": () => \"time\",\n \"TR\": () => \"row\",\n \"UL\": () => \"list\"\n};\nfunction isHeaderCell(element) {\n return !!element && elementSafeTagName(element) === \"TH\";\n}\nfunction isNonEmptyDataCell(element) {\n var _a;\n if (!element || elementSafeTagName(element) !== \"TD\")\n return false;\n return !!(((_a = element.textContent) == null ? void 0 : _a.trim()) || element.children.length > 0);\n}\nvar kPresentationInheritanceParents = {\n \"DD\": [\"DL\", \"DIV\"],\n \"DIV\": [\"DL\"],\n \"DT\": [\"DL\", \"DIV\"],\n \"LI\": [\"OL\", \"UL\"],\n \"TBODY\": [\"TABLE\"],\n \"TD\": [\"TR\"],\n \"TFOOT\": [\"TABLE\"],\n \"TH\": [\"TR\"],\n \"THEAD\": [\"TABLE\"],\n \"TR\": [\"THEAD\", \"TBODY\", \"TFOOT\", \"TABLE\"]\n};\nfunction getImplicitAriaRole(element) {\n var _a;\n const implicitRole = ((_a = kImplicitRoleByTagName[elementSafeTagName(element)]) == null ? void 0 : _a.call(kImplicitRoleByTagName, element)) || \"\";\n if (!implicitRole)\n return null;\n let ancestor = element;\n while (ancestor) {\n const parent = parentElementOrShadowHost(ancestor);\n const parents = kPresentationInheritanceParents[elementSafeTagName(ancestor)];\n if (!parents || !parent || !parents.includes(elementSafeTagName(parent)))\n break;\n const parentExplicitRole = getExplicitAriaRole(parent);\n if ((parentExplicitRole === \"none\" || parentExplicitRole === \"presentation\") && !hasPresentationConflictResolution(parent, parentExplicitRole))\n return parentExplicitRole;\n ancestor = parent;\n }\n return implicitRole;\n}\nvar validRoles = [\n \"alert\",\n \"alertdialog\",\n \"application\",\n \"article\",\n \"banner\",\n \"blockquote\",\n \"button\",\n \"caption\",\n \"cell\",\n \"checkbox\",\n \"code\",\n \"columnheader\",\n \"combobox\",\n \"complementary\",\n \"contentinfo\",\n \"definition\",\n \"deletion\",\n \"dialog\",\n \"directory\",\n \"document\",\n \"emphasis\",\n \"feed\",\n \"figure\",\n \"form\",\n \"generic\",\n \"grid\",\n \"gridcell\",\n \"group\",\n \"heading\",\n \"img\",\n \"insertion\",\n \"link\",\n \"list\",\n \"listbox\",\n \"listitem\",\n \"log\",\n \"main\",\n \"mark\",\n \"marquee\",\n \"math\",\n \"meter\",\n \"menu\",\n \"menubar\",\n \"menuitem\",\n \"menuitemcheckbox\",\n \"menuitemradio\",\n \"navigation\",\n \"none\",\n \"note\",\n \"option\",\n \"paragraph\",\n \"presentation\",\n \"progressbar\",\n \"radio\",\n \"radiogroup\",\n \"region\",\n \"row\",\n \"rowgroup\",\n \"rowheader\",\n \"scrollbar\",\n \"search\",\n \"searchbox\",\n \"separator\",\n \"slider\",\n \"spinbutton\",\n \"status\",\n \"strong\",\n \"subscript\",\n \"superscript\",\n \"switch\",\n \"tab\",\n \"table\",\n \"tablist\",\n \"tabpanel\",\n \"term\",\n \"textbox\",\n \"time\",\n \"timer\",\n \"toolbar\",\n \"tooltip\",\n \"tree\",\n \"treegrid\",\n \"treeitem\"\n];\nfunction getExplicitAriaRole(element) {\n const roles = (element.getAttribute(\"role\") || \"\").split(\" \").map((role) => role.trim());\n return roles.find((role) => validRoles.includes(role)) || null;\n}\nfunction hasPresentationConflictResolution(element, role) {\n return hasGlobalAriaAttribute(element, role) || isFocusable(element);\n}\nfunction getAriaRole(element) {\n const explicitRole = getExplicitAriaRole(element);\n if (!explicitRole)\n return getImplicitAriaRole(element);\n if (explicitRole === \"none\" || explicitRole === \"presentation\") {\n const implicitRole = getImplicitAriaRole(element);\n if (hasPresentationConflictResolution(element, implicitRole))\n return implicitRole;\n }\n return explicitRole;\n}\nfunction getIdRefs(element, ref) {\n if (!ref)\n return [];\n const root = enclosingShadowRootOrDocument(element);\n if (!root)\n return [];\n try {\n const ids = ref.split(\" \").filter((id) => !!id);\n const result = [];\n for (const id of ids) {\n const firstElement = root.querySelector(\"#\" + CSS.escape(id));\n if (firstElement && !result.includes(firstElement))\n result.push(firstElement);\n }\n return result;\n } catch (e) {\n return [];\n }\n}\nfunction isNativelyDisabled(element) {\n const isNativeFormControl = [\"BUTTON\", \"INPUT\", \"SELECT\", \"TEXTAREA\", \"OPTION\", \"OPTGROUP\"].includes(elementSafeTagName(element));\n return isNativeFormControl && (element.hasAttribute(\"disabled\") || belongsToDisabledOptGroup(element) || belongsToDisabledFieldSet(element));\n}\nfunction belongsToDisabledOptGroup(element) {\n return elementSafeTagName(element) === \"OPTION\" && !!element.closest(\"OPTGROUP[DISABLED]\");\n}\nfunction belongsToDisabledFieldSet(element) {\n const fieldSetElement = element == null ? void 0 : element.closest(\"FIELDSET[DISABLED]\");\n if (!fieldSetElement)\n return false;\n const legendElement = fieldSetElement.querySelector(\":scope > LEGEND\");\n return !legendElement || !legendElement.contains(element);\n}\nvar inputTypeToRole = {\n \"button\": \"button\",\n \"checkbox\": \"checkbox\",\n \"image\": \"button\",\n \"number\": \"spinbutton\",\n \"radio\": \"radio\",\n \"range\": \"slider\",\n \"reset\": \"button\",\n \"submit\": \"button\"\n};\n\n// packages/injected/src/recorder/skyramp/ScopingHandler.ts\nvar LOG_PREFIX = \"[Scoping]\";\nfunction isLogEnabled() {\n try {\n if (typeof window !== \"undefined\" && window.__SKYRAMP_DEBUG__) {\n return true;\n }\n if (typeof localStorage !== \"undefined\" && localStorage.getItem(\"SKYRAMP_DEBUG\") === \"true\") {\n return true;\n }\n } catch {\n }\n return false;\n}\nfunction log(...args) {\n if (isLogEnabled()) {\n console.log(LOG_PREFIX, ...args);\n }\n}\nfunction logGroup(label) {\n if (isLogEnabled()) {\n console.group(`${LOG_PREFIX} ${label}`);\n }\n}\nfunction logGroupEnd() {\n if (isLogEnabled()) {\n console.groupEnd();\n }\n}\nfunction logTable(data) {\n if (isLogEnabled()) {\n console.table(data);\n }\n}\nvar CSS_ITEM_PATTERNS = [\n /card/i,\n // product-card, hot-product-card, card-item\n /item/i,\n // list-item, grid-item, menu-item\n /tile/i,\n // product-tile, image-tile\n /cell/i,\n // grid-cell, table-cell\n /row(?!s)/i,\n // data-row, table-row (but not \"rows\")\n /entry/i,\n // feed-entry, log-entry\n /result/i,\n // search-result, result-item\n /post/i,\n // blog-post, feed-post\n /product/i,\n // product, product-listing\n /option/i\n // select-option, dropdown-option (Sentry uses [role=\"option\"])\n];\nvar CSS_SKIP_PATTERNS = [\n /^col-/i,\n // Bootstrap columns: col-md-4, col-12\n /^row$/i,\n // Bootstrap row (exact match)\n /^container/i,\n // container, container-fluid\n /^px-/i,\n /^py-/i,\n // Padding utilities\n /^mx-/i,\n /^my-/i,\n // Margin utilities\n /^m-/i,\n /^p-/i,\n // Single margin/padding\n /^d-/i,\n // Display utilities: d-flex, d-none\n /^flex/i,\n // Flexbox utilities\n /^grid$/i,\n // Grid utility\n /^text-/i,\n // Text utilities\n /^bg-/i,\n // Background utilities\n /^border/i,\n // Border utilities\n /^rounded/i,\n // Border radius utilities\n /^shadow/i,\n // Shadow utilities\n /^w-/i,\n /^h-/i,\n // Width/height utilities\n /^css-/i,\n // CSS-in-JS: css-xxxxx\n /^styled-/i,\n // Styled-components\n /^sc-/i,\n // Styled-components\n /^emotion-/i,\n // Emotion CSS-in-JS\n /^MuiGrid/i,\n // Material-UI grid\n /^MuiBox/i,\n // Material-UI box\n /--[a-f0-9]{16,}$/i,\n // CSS-in-JS hash suffix: class--d5fc23da2c7ac21a\n /__[a-f0-9]{16,}$/i,\n // CSS-in-JS hash suffix: class__d5fc23da2c7ac21a\n /_[a-f0-9]{16,}$/i,\n // CSS-in-JS hash suffix: class_d5fc23da2c7ac21a\n // Sentry/Emotion short-form CSS-in-JS patterns\n /^app-[a-z0-9]+$/i,\n // Emotion: app-r5ldb0\n /^e[a-z0-9]{6,}\\d+$/i\n // Emotion: e1s9zdwb0, ebcy13q0\n];\nvar ScopingHandler = class {\n constructor(injectedScript) {\n this._injectedScript = injectedScript;\n log(\"ScopingHandler initialized (with CSS class pattern support)\");\n }\n // ==========================================================================\n // Main Hook Entry Point\n // ==========================================================================\n /**\n * Main hook - generates scoped selector using .nth() pattern:\n * container >> nth=N >> relativeSelector\n */\n applyScopingHook(element, selector, elements) {\n logGroup(`Analyzing: ${selector}`);\n const needsScoping = this._needsScoping(element, selector, elements);\n log(\"Needs scoping:\", needsScoping.needed, \"| Reason:\", needsScoping.reason);\n if (!needsScoping.needed) {\n const stableId2 = this._tryStableIdSelector(element, selector);\n if (stableId2) {\n log(\"Preferring stable ID selector:\", stableId2.selector);\n logGroupEnd();\n return stableId2;\n }\n logGroupEnd();\n return null;\n }\n const stableId = this._tryStableIdSelector(element, selector);\n if (stableId) {\n log(\"Using stable ID selector:\", stableId.selector);\n logGroupEnd();\n return stableId;\n }\n const linkSelector = this._tryLinkSelector(element, selector);\n if (linkSelector) {\n log(\"Using link selector:\", linkSelector.selector);\n logGroupEnd();\n return linkSelector;\n }\n const container = this._findContainer(element);\n if (!container) {\n log(\"No container found\");\n if (this._hasDynamicSelector(selector)) {\n const alternative = this._generateAlternativeSelector(element, selector);\n if (alternative) {\n log(\"Using alternative selector:\", alternative.selector);\n logGroupEnd();\n return alternative;\n }\n }\n logGroupEnd();\n return null;\n }\n log(\"Container:\", container.selector);\n const containerIndex = this._getContainerIndex(container.element, container.selector);\n if (containerIndex === null) {\n log(\"Cannot determine container index\");\n logGroupEnd();\n return null;\n }\n log(\"Container index:\", containerIndex);\n const relativeSelector = this._generateRelativeSelector(container.element, element);\n if (!relativeSelector) {\n log(\"No relative selector found\");\n logGroupEnd();\n return null;\n }\n log(\"Relative selector:\", relativeSelector);\n const isFormContainer = this._isFormContainer(container.selector);\n let scopedSelector;\n let usesTextFilter = false;\n if (isFormContainer) {\n scopedSelector = `${container.selector} >> ${relativeSelector}`;\n log(\"Using form container selector (no nth):\", scopedSelector);\n } else {\n const hasFilter = this._getRowHasFilter(container.element, container.selector);\n if (hasFilter) {\n scopedSelector = `${container.selector} >> internal:has=${hasFilter} >> ${relativeSelector}`;\n usesTextFilter = true;\n log(\"Using has-filter selector:\", scopedSelector);\n } else {\n const textFilter = this._getTextFilterForContainer(container.element, container.selector);\n if (textFilter) {\n scopedSelector = `${container.selector} >> internal:has-text=\"${this._escapeTextFilter(textFilter)}\"i >> ${relativeSelector}`;\n usesTextFilter = true;\n log(\"Using text-filtered selector:\", scopedSelector);\n } else {\n const rowAnchor = this._getRowAnchorSelector(container.element, element);\n if (rowAnchor) {\n scopedSelector = rowAnchor;\n usesTextFilter = true;\n log(\"Using row-anchored selector:\", scopedSelector);\n } else {\n scopedSelector = `${container.selector} >> nth=${containerIndex} >> ${relativeSelector}`;\n log(\"Using nth-based selector:\", scopedSelector);\n }\n }\n }\n }\n const verification = this._verifySelector(scopedSelector);\n log(\"Verification:\", verification.valid ? \"PASS\" : \"FAIL\", \"| Matches:\", verification.count);\n if (!verification.valid) {\n if (isFormContainer && verification.count >= 1) {\n log(\"Form container verification relaxed - using selector despite multiple matches\");\n } else {\n log(\"Verification failed, using original\");\n logGroupEnd();\n return null;\n }\n }\n const result = {\n selector: scopedSelector,\n container: container.element,\n elements: verification.elements,\n strategy: \"nth\",\n description: isFormContainer ? `${container.selector} >> ${relativeSelector}` : `${container.selector}.nth(${containerIndex}) >> ${relativeSelector}`,\n containerSelector: container.selector,\n containerIndex,\n relativeSelector,\n isFormContainer,\n usesTextFilter\n };\n logTable({\n \"Original\": selector,\n \"Scoped\": scopedSelector,\n \"Container\": container.selector,\n \"Index\": containerIndex,\n \"Relative\": relativeSelector\n });\n logGroupEnd();\n return result;\n }\n // ==========================================================================\n // Step 1: Check if Scoping Needed\n // ==========================================================================\n _needsScoping(element, selector, elements) {\n log(\"Input match set:\", { selector, count: elements.length });\n if (elements.length > 1) {\n return { needed: true, reason: `Non-unique: ${elements.length} elements` };\n }\n if (this._hasDynamicSelector(selector)) {\n return { needed: true, reason: \"Dynamic selector needs replacement\" };\n }\n const stateCheck = this._hasStateDependentName(element, selector);\n if (stateCheck.isStateDependent) {\n return { needed: true, reason: stateCheck.reason };\n }\n const repeatingRoles = [\"gridcell\", \"row\", \"listitem\", \"option\", \"treeitem\", \"menuitem\", \"cell\"];\n const elementRole = element.getAttribute(\"role\");\n if (elementRole && repeatingRoles.includes(elementRole)) {\n const container = this._findRepeatingContainer(element);\n if (container) {\n return { needed: true, reason: `Repeating role \"${elementRole}\" inside container: ${container.selector}` };\n }\n }\n const closestTd = element.tagName === \"TD\" ? element : element.closest(\"td\");\n if (closestTd) {\n const tr = closestTd.closest(\"tr\");\n if (tr) {\n const tbody = tr.parentElement;\n if (tbody && (tbody.tagName === \"TBODY\" || tbody.tagName === \"TABLE\")) {\n const rows = tbody.querySelectorAll(\":scope > tr\");\n if (rows.length > 1) {\n return { needed: true, reason: `Element inside <td> in <tr> with ${rows.length} sibling rows` };\n }\n }\n }\n }\n return { needed: false, reason: \"Selector is unique and not in repeating context\" };\n }\n // ==========================================================================\n // Stable ID Preference\n // ==========================================================================\n /**\n * Check if an element ID looks dynamic (generated at runtime).\n * Dynamic IDs change across sessions/page loads so they make fragile selectors.\n *\n * NOTE: The canonical source of truth for these rules is\n * `packages/playwright/src/dom-analyzer/dynamicId.ts` (exported as\n * `isDynamicId`). This local copy exists because cross-package imports\n * from injected to playwright are not used elsewhere in the repo and\n * add resolution risk. When adding or adjusting a rule here, update\n * the shared module in lockstep; drift will silently degrade blueprint\n * collision-resolution output quality (Bug 1.3).\n */\n _isDynamicId(id) {\n if (/^react-aria\\d+/.test(id)) return true;\n if (/^mui-\\d+/.test(id)) return true;\n if (/^(mat|cdk)-[a-z]+-\\d+$/.test(id)) return true;\n if (/[-_]\\d+$/.test(id)) return true;\n if (/\\d{2,}[_-][a-zA-Z]/.test(id)) return true;\n if (/^\\d+$/.test(id)) return true;\n if (/\\d{4,}$/.test(id)) return true;\n if (/[-_][0-9a-f]{6,}$/i.test(id)) return true;\n const shortHexMatch = id.match(/[-_]([0-9a-f]{3,5})$/i);\n if (shortHexMatch && /[0-9]/.test(shortHexMatch[1])) return true;\n if (/[a-zA-Z][0-9]{3,}$/.test(id)) return true;\n if (id.includes(\":\")) return true;\n if (/^.+__search_[a-zA-Z0-9]{4,}$/.test(id)) return true;\n return false;\n }\n /**\n * When the element has a stable (non-dynamic) ID, prefer #id over a role\n * selector. Role selectors can become ambiguous when page state differs\n * between recording and playback.\n * Only replaces role-based selectors — if the original is already ID-based\n * or testid-based, leave it alone.\n */\n _tryStableIdSelector(element, selector) {\n const id = element.id;\n if (!id) return null;\n if (!selector.startsWith(\"internal:role=\")) return null;\n if (this._isDynamicId(id)) {\n log(\"Skipping dynamic ID:\", id);\n return null;\n }\n const idSelector = `#${CSS.escape(id)}`;\n const verification = this._verifySelector(idSelector);\n if (!verification.valid || verification.count !== 1) {\n log(\"ID selector not unique:\", idSelector, \"matches:\", verification.count);\n return null;\n }\n return {\n selector: idSelector,\n container: null,\n elements: verification.elements,\n strategy: \"alternative\",\n description: `Stable ID preferred over role selector`,\n containerSelector: \"\",\n containerIndex: -1,\n relativeSelector: \"\",\n isAlternativeSelector: true\n };\n }\n /**\n * Check if selector contains dynamic/fragile patterns that should be replaced\n * Examples: #contextmenutarget19, #item-42, #row_123, internal:attr=[id=\"a-text-input_18\"]\n * Sentry: #react-aria9765209213-_r_nj_\n */\n _hasDynamicSelector(selector) {\n log(\"_hasDynamicSelector checking:\", selector);\n if (/react-aria\\d+/.test(selector)) {\n log(\"MATCHED: React-Aria dynamic ID\");\n return true;\n }\n if (/__search_[a-zA-Z0-9]{4,}/.test(selector)) {\n log(\"MATCHED: Vue Tables dynamic search ID\");\n return true;\n }\n if (/#[a-zA-Z_-]*\\d+/.test(selector)) {\n log(\"MATCHED: Dynamic ID with numeric suffix (CSS)\");\n return true;\n }\n if (/internal:attr=\\[id=\"[a-zA-Z_-]*\\d+\"\\]/.test(selector)) {\n log(\"MATCHED: Dynamic ID with numeric suffix (internal:attr)\");\n return true;\n }\n if (/\\[id=\"[a-zA-Z_-]*\\d+\"\\]/.test(selector)) {\n log(\"MATCHED: Dynamic ID with numeric suffix (attribute selector)\");\n return true;\n }\n if (/\\[id=\"[^\"]*\\d{2,}[_-][a-zA-Z][^\"]*\"\\]/.test(selector)) {\n log(\"MATCHED: Dynamic ID with mid-string counter (attribute selector)\");\n return true;\n }\n if (/internal:attr=\\[id=\"[^\"]*\\d{2,}[_-][a-zA-Z][^\"]*\"\\]/.test(selector)) {\n log(\"MATCHED: Dynamic ID with mid-string counter (internal:attr)\");\n return true;\n }\n if (/\\.(app-[a-z0-9]+|e[a-z0-9]{6,}[0-9]+)/.test(selector)) {\n log(\"MATCHED: CSS-in-JS generated class (Emotion)\");\n return true;\n }\n const childCombinatorCount = (selector.match(/>/g) || []).length;\n if (childCombinatorCount >= 4) {\n log(\"MATCHED: Long CSS path with\", childCombinatorCount, \"child combinators\");\n return true;\n }\n if (/:nth-child\\(\\d+\\)/.test(selector)) {\n log(\"MATCHED: Contains :nth-child() pattern\");\n return true;\n }\n if (/\\[data-testid=\"[^\"]*[-_]\\d+\"\\]/.test(selector) || /\\[data-test-id=\"[^\"]*[-_]\\d+\"\\]/.test(selector)) {\n log(\"MATCHED: TestId with numeric suffix (attribute selector)\");\n return true;\n }\n if (/getByTestId\\(['\"][^'\"]*[-_]\\d+['\"]\\)/.test(selector)) {\n log(\"MATCHED: TestId with numeric suffix (getByTestId)\");\n return true;\n }\n if (/internal:testid=.*[-_]\\d+/.test(selector)) {\n log(\"MATCHED: TestId with numeric suffix (internal:testid)\");\n return true;\n }\n if (/\\[name=\"\\d+\"[is]?\\]/.test(selector) || /internal:text=\"\\d+\"[is]?/.test(selector)) {\n log(\"MATCHED: All-numeric text content in selector\");\n return true;\n }\n if (/\\[name=\"[^\"]*\\d{8,}[^\"]*\"/.test(selector) || /internal:text=\"[^\"]*\\d{8,}[^\"]*\"/.test(selector)) {\n log(\"MATCHED: Long digit sequence in text content (timestamp/generated)\");\n return true;\n }\n if (/(?:\\[name|internal:text)=\"\\/?\\d{1,2}\\/\\d{1,2}\"/.test(selector)) {\n log(\"MATCHED: Partial date pattern in text content\");\n return true;\n }\n const textFragments = [...selector.matchAll(/(?:\\[name|internal:text)=\"([^\"]*)\"/g)].map((m) => m[1]);\n if (textFragments.some((t) => this._hasVolatileText(t))) {\n log(\"MATCHED: Month-name date in text content (volatile)\");\n return true;\n }\n log(\"No dynamic patterns found\");\n return false;\n }\n /**\n * Check if element's accessible name might be state-dependent (hover, focus, etc.)\n * This detects cases like Box.com where hovering shows a checkbox that changes\n * the accessible name from \"Personal Folder\" to \"Select Personal Folder\".\n *\n * Strategy: Check if our element's accessible name CONTAINS a substring that\n * matches siblings' accessible names. This indicates the name might be\n * augmented by hover state.\n *\n * Example (Box.com - needs scoping):\n * - Element name: \"Select Personal Folder\" (unique at recording, but hover-dependent)\n * - Sibling names: \"Personal Folder\", \"Personal Folder\", \"Personal Folder\"\n * - \"Select Personal Folder\" contains \"Personal Folder\" → state-dependent\n *\n * Counter-example (Knode.ai - should NOT be scoped):\n * - Element name: \"Teams\"\n * - Sibling names: \"Dashboard\", \"Calls\", \"Users\", \"Integrations\"\n * - \"Teams\" doesn't contain any sibling name → NOT state-dependent\n */\n _hasStateDependentName(element, selector) {\n const nameMatch = selector.match(/internal:role=(\\w+)\\[name=[\"'](.+?)[\"'][is]?\\]/);\n if (!nameMatch) {\n return { isStateDependent: false, reason: \"Not a role selector with name\" };\n }\n const role = nameMatch[1];\n const elementName = nameMatch[2];\n if (!elementName || elementName.length < 3) {\n return { isStateDependent: false, reason: \"Name too short\" };\n }\n log(\"Checking state-dependent name:\", { role, name: elementName });\n const siblingNames = this._getSiblingAccessibleNames(element, role);\n if (siblingNames.length === 0) {\n log(\"No siblings found for state check\");\n return { isStateDependent: false, reason: \"No siblings with same role\" };\n }\n log(\"Sibling names:\", siblingNames);\n for (const siblingName of siblingNames) {\n if (siblingName.length >= 3 && elementName.length > siblingName.length) {\n if (elementName.toLowerCase().includes(siblingName.toLowerCase())) {\n log(\"State-dependent name detected:\", elementName, \"contains\", siblingName);\n return {\n isStateDependent: true,\n reason: `Name \"${elementName}\" contains sibling name \"${siblingName}\" - likely hover-dependent`\n };\n }\n }\n }\n return { isStateDependent: false, reason: \"Name is unique among siblings\" };\n }\n /**\n * Get accessible names of sibling elements with the same role\n * Only looks within the same parent container (not the entire document)\n * to avoid false positives from unrelated elements elsewhere on the page.\n * Excludes the target element and elements with duplicate names.\n */\n _getSiblingAccessibleNames(element, role) {\n const names = [];\n const seen = /* @__PURE__ */ new Set();\n try {\n const containerSelectors = [\"ul\", \"ol\", \"nav\", '[role=\"list\"]', '[role=\"navigation\"]', '[role=\"menu\"]', '[role=\"tablist\"]', '[role=\"grid\"]', '[role=\"row\"]'];\n let container = element.parentElement;\n let searchScope = element.ownerDocument.body;\n let depth = 0;\n while (container && depth < 5) {\n const tagLower = container.tagName.toLowerCase();\n const containerRole = container.getAttribute(\"role\");\n if (containerSelectors.some((sel) => {\n var _a;\n if (sel.startsWith(\"[role=\")) {\n const roleVal = (_a = sel.match(/\\[role=\"(.+)\"\\]/)) == null ? void 0 : _a[1];\n return containerRole === roleVal;\n }\n return tagLower === sel;\n })) {\n searchScope = container;\n break;\n }\n container = container.parentElement;\n depth++;\n }\n log(\"Sibling search scope:\", searchScope.tagName, searchScope.className);\n const selector = `[role=\"${role}\"]`;\n const siblings = searchScope.querySelectorAll(selector);\n for (const sibling of siblings) {\n if (sibling === element) continue;\n const name = this._getAccessibleName(sibling);\n if (name && name.length >= 2 && !seen.has(name.toLowerCase())) {\n seen.add(name.toLowerCase());\n names.push(name);\n }\n }\n } catch (e) {\n log(\"Error getting sibling names:\", e);\n }\n return names;\n }\n /**\n * Check if element is inside a repeating container (multiple siblings with same testid/role/class)\n */\n _findRepeatingContainer(element) {\n const itemPatterns = [\n /^grid[-_]?view[-_]?item$/i,\n /^gridcell$/i,\n /^list[-_]?item$/i,\n /^row[-_]?item$/i,\n /^item$/i,\n /^card$/i,\n /^tile$/i\n ];\n const skipPatterns = [\n /^gridview$/i,\n /^grid[-_]?view$/i,\n /^listview$/i,\n /^list[-_]?view$/i,\n /^container$/i,\n /^wrapper$/i,\n /^content$/i,\n /^main$/i\n ];\n let current = element.parentElement;\n while (current && current !== element.ownerDocument.body) {\n const testId = this._getTestId(current);\n if (testId && !skipPatterns.some((p) => p.test(testId))) {\n if (itemPatterns.some((p) => p.test(testId))) {\n const selector = this._buildTestIdSelector(current, testId);\n const siblings = current.ownerDocument.querySelectorAll(selector);\n if (siblings.length > 1) {\n return { element: current, selector };\n }\n }\n }\n const role = current.getAttribute(\"role\");\n if (role && [\"row\", \"gridcell\", \"listitem\", \"option\", \"treeitem\", \"menuitem\"].includes(role)) {\n const selector = `[role=${this._quoteCSSAttributeValue(role)}]`;\n const siblings = current.ownerDocument.querySelectorAll(selector);\n if (siblings.length > 1) {\n return { element: current, selector };\n }\n }\n const componentInfo = this._getComponentName(current);\n if (componentInfo && this._isRepeatingComponentName(componentInfo.name)) {\n const selector = `[${componentInfo.attr}=${this._quoteCSSAttributeValue(componentInfo.name)}]`;\n try {\n const siblings = current.ownerDocument.querySelectorAll(selector);\n if (siblings.length > 1 && siblings.length < 100) {\n log(\"Found component container:\", componentInfo.name, \"via\", componentInfo.attr, \"with\", siblings.length, \"siblings\");\n return { element: current, selector };\n }\n } catch {\n }\n }\n const cssContainer = this._findRepeatingContainerByClass(current);\n if (cssContainer) {\n return cssContainer;\n }\n current = current.parentElement;\n }\n return null;\n }\n /**\n * Check if element has CSS classes that indicate a repeating container\n * Returns the container info if found, null otherwise\n *\n * IMPORTANT: Uses TAG NAME as selector instead of CSS class for stability.\n * CSS classes are only used to DETECT repeating containers, but the\n * selector uses the stable tag name. Text filtering handles uniqueness.\n */\n _findRepeatingContainerByClass(element) {\n const classList = Array.from(element.classList);\n for (const cls of classList) {\n if (CSS_SKIP_PATTERNS.some((p) => p.test(cls))) {\n continue;\n }\n if (CSS_ITEM_PATTERNS.some((p) => p.test(cls))) {\n const cssSelector = `.${this._escapeCSS(cls)}`;\n try {\n const siblings = element.ownerDocument.querySelectorAll(cssSelector);\n if (siblings.length > 1 && siblings.length < 100) {\n const tagSelector = element.tagName.toLowerCase();\n log(\"Found CSS class container:\", cls, \"with\", siblings.length, \"siblings, using tag:\", tagSelector);\n return { element, selector: tagSelector };\n }\n } catch {\n continue;\n }\n }\n }\n return null;\n }\n /**\n * Escape CSS class name for use in selector\n * Handles special characters that need escaping\n */\n _escapeCSS(value) {\n return escapeCSS(value);\n }\n /**\n * Quote and escape a value for use in CSS attribute selectors\n * Escapes backslashes and double quotes to prevent malformed selectors\n * Example: value with \"quotes\" -> \"value with \\\"quotes\\\"\"\n */\n _quoteCSSAttributeValue(text) {\n return quoteCSSAttributeValue(text);\n }\n /**\n * Get test ID from element - supports both data-testid and data-test-id (Sentry uses hyphen)\n */\n _getTestId(element) {\n return element.getAttribute(\"data-testid\") || element.getAttribute(\"data-test-id\");\n }\n /**\n * Build a selector for test ID - uses whichever attribute the element has\n */\n _buildTestIdSelector(element, testId) {\n if (element.getAttribute(\"data-testid\") === testId) {\n return `[data-testid=${this._quoteCSSAttributeValue(testId)}]`;\n }\n return `[data-test-id=${this._quoteCSSAttributeValue(testId)}]`;\n }\n /**\n * Get component name from element using common data-* attributes\n * Supports: data-component, data-sentry-component, data-react-component\n */\n _getComponentName(element) {\n const componentAttrs = [\"data-component\", \"data-sentry-component\", \"data-react-component\"];\n for (const attr of componentAttrs) {\n const value = element.getAttribute(attr);\n if (value) {\n return { name: value, attr };\n }\n }\n return null;\n }\n /**\n * Check if component name indicates a repeating container\n * Based on common naming conventions (Card, Item, Row, etc.)\n */\n _isRepeatingComponentName(componentName) {\n const repeatingPatterns = [\n /Card$/i,\n // DashboardCard, ProductCard\n /Item$/i,\n // ListItem, GridItem\n /Link$/i,\n // NavLink (when in lists)\n /Row$/i,\n // TableRow, DataRow\n /Tile$/i,\n // GridTile, ImageTile\n /Option$/i,\n // SelectOption, DropdownOption\n /Entry$/i\n // FeedEntry, LogEntry\n ];\n return repeatingPatterns.some((p) => p.test(componentName));\n }\n // ==========================================================================\n // Step 2: Find Container\n // ==========================================================================\n _findContainer(element) {\n const itemPatterns = [\n /^grid[-_]?view[-_]?item$/i,\n /^gridcell$/i,\n /^list[-_]?item$/i,\n /^row[-_]?item$/i,\n /^item$/i,\n /^card$/i,\n /^tile$/i\n ];\n const formContainerPatterns = [\n /[-_]input$/i,\n // edit-name-input, search-input\n /[-_]field$/i,\n // name-field, email-field\n /[-_]btn$/i,\n // edit-btn, submit-btn\n /[-_]button$/i,\n // save-button, cancel-button\n /[-_]control$/i,\n // date-control, select-control\n /^input[-_]/i,\n // input-name, input-email\n /^field[-_]/i\n // field-name, field-email\n ];\n const skipPatterns = [\n /^gridview$/i,\n /^grid[-_]?view$/i,\n /^listview$/i,\n /^list[-_]?view$/i,\n /^container$/i,\n /^wrapper$/i,\n /^content$/i,\n /^main$/i\n ];\n let current = element.parentElement;\n let candidate = null;\n let cssCandidate = null;\n while (current && current !== element.ownerDocument.body) {\n const testId = this._getTestId(current);\n if (testId) {\n if (skipPatterns.some((p) => p.test(testId))) {\n current = current.parentElement;\n continue;\n }\n const dynamicTestId = this._isFragileTestId(testId);\n if (!dynamicTestId && itemPatterns.some((p) => p.test(testId))) {\n return { element: current, selector: this._buildTestIdSelector(current, testId) };\n }\n if (!dynamicTestId && formContainerPatterns.some((p) => p.test(testId))) {\n log(\"Found form container by testid pattern:\", testId);\n return { element: current, selector: this._buildTestIdSelector(current, testId) };\n }\n if (!candidate && !dynamicTestId) {\n candidate = { element: current, selector: this._buildTestIdSelector(current, testId) };\n }\n }\n const role = current.getAttribute(\"role\");\n if (role && [\"row\", \"gridcell\", \"listitem\", \"option\", \"treeitem\", \"menuitem\"].includes(role)) {\n if (!candidate) {\n candidate = { element: current, selector: `[role=${this._quoteCSSAttributeValue(role)}]` };\n }\n }\n if (current.tagName === \"TR\") {\n const tbody = current.parentElement;\n if (tbody && (tbody.tagName === \"TBODY\" || tbody.tagName === \"TABLE\")) {\n const rows = tbody.querySelectorAll(\":scope > tr\");\n if (rows.length > 1 && !candidate) {\n candidate = { element: current, selector: \"tr\" };\n }\n }\n }\n const componentInfo = this._getComponentName(current);\n if (componentInfo && this._isRepeatingComponentName(componentInfo.name)) {\n const selector = `[${componentInfo.attr}=${this._quoteCSSAttributeValue(componentInfo.name)}]`;\n try {\n const siblings = current.ownerDocument.querySelectorAll(selector);\n if (siblings.length > 1 && siblings.length < 100) {\n log(\"Found component container:\", componentInfo.name, \"via\", componentInfo.attr, \"with\", siblings.length, \"siblings\");\n return { element: current, selector };\n }\n } catch {\n }\n }\n if (!cssCandidate) {\n const cssContainer = this._findRepeatingContainerByClass(current);\n if (cssContainer) {\n cssCandidate = cssContainer;\n }\n }\n current = current.parentElement;\n }\n return candidate || cssCandidate;\n }\n // ==========================================================================\n // Check if Container is a Form Container (unique, not repeating)\n // ==========================================================================\n /**\n * Check if the container selector matches form container patterns.\n * Form containers are unique wrappers for form elements (inputs, buttons, etc.)\n * They don't need nth() indexing because they're not repeating elements.\n */\n _isFormContainer(containerSelector) {\n const formContainerPatterns = [\n /-input\"\\]$/i,\n // [data-testid=\"edit-name-input\"]\n /-field\"\\]$/i,\n // [data-testid=\"name-field\"]\n /-btn\"\\]$/i,\n // [data-testid=\"edit-btn\"]\n /-button\"\\]$/i,\n // [data-testid=\"save-button\"]\n /-control\"\\]$/i,\n // [data-testid=\"date-control\"]\n /\\[data-test-?id=\"input-/i,\n // [data-testid=\"input-name\"] or [data-test-id=\"input-name\"]\n /\\[data-test-?id=\"field-/i\n // [data-testid=\"field-name\"] or [data-test-id=\"field-name\"]\n ];\n const isForm = formContainerPatterns.some((pattern) => pattern.test(containerSelector));\n log(\"_isFormContainer:\", containerSelector, \"=\", isForm);\n return isForm;\n }\n // ==========================================================================\n // Step 3: Get Container Index\n // ==========================================================================\n _getContainerIndex(container, containerSelector) {\n try {\n const all = container.ownerDocument.querySelectorAll(containerSelector);\n const idx = Array.from(all).indexOf(container);\n return idx !== -1 ? idx : null;\n } catch {\n return null;\n }\n }\n // ==========================================================================\n // Text-Based Container Filtering (for row-like containers)\n // ==========================================================================\n /**\n * Get unique identifying text for a container to use in has-text filter\n * Works with ANY repeating container type (rows, options, list items, etc.)\n * Returns null if no unique text can be found (fall back to nth)\n */\n _getTextFilterForContainer(container, containerSelector) {\n var _a;\n const role = container.getAttribute(\"role\");\n let identifyingText = null;\n if (role === \"row\" || container.tagName === \"TR\" || container.classList.contains(\"a-data-table__row\")) {\n identifyingText = this._getRowIdentifyingText(container);\n } else {\n identifyingText = this._getContainerIdentifyingText(container);\n }\n if (!identifyingText) {\n log(\"Text filter: No identifying text found\");\n return null;\n }\n const allContainers = container.ownerDocument.querySelectorAll(containerSelector);\n let matchCount = 0;\n for (const c of allContainers) {\n if ((_a = c.textContent) == null ? void 0 : _a.includes(identifyingText)) {\n matchCount++;\n }\n }\n if (matchCount === 1) {\n log(\"Text filter: Found unique text:\", identifyingText);\n return identifyingText;\n }\n log(\"Text filter: Text not unique, found in\", matchCount, \"containers\");\n return null;\n }\n /**\n * Extract identifying text from any container type (not just rows)\n * Used for [role=\"option\"], [role=\"listitem\"], etc.\n * Searches multiple levels deep for meaningful text\n */\n _getContainerIdentifyingText(container) {\n var _a;\n const directText = this._getDirectTextContent(container);\n if (directText && directText.length >= 2 && directText.length <= 100 && !this._isGenericText(directText)) {\n return directText;\n }\n const ariaLabel = container.getAttribute(\"aria-label\");\n if (ariaLabel && ariaLabel.length >= 2 && ariaLabel.length <= 100 && !this._isGenericText(ariaLabel)) {\n return ariaLabel;\n }\n const textElements = container.querySelectorAll(\"span, div, p, h1, h2, h3, h4, h5, h6\");\n for (const el of textElements) {\n const text = this._getDirectTextContent(el);\n if (text && text.length >= 2 && text.length <= 100 && !this._isGenericText(text)) {\n return text;\n }\n }\n const labeledElement = container.querySelector(\"[aria-label]\");\n if (labeledElement) {\n const label = labeledElement.getAttribute(\"aria-label\");\n if (label && label.length >= 2 && label.length <= 100 && !this._isGenericText(label)) {\n return label;\n }\n }\n const fullText = (_a = container.textContent) == null ? void 0 : _a.trim();\n if (fullText && fullText.length >= 2 && fullText.length <= 100 && !this._isGenericText(fullText)) {\n return fullText;\n }\n return null;\n }\n /**\n * Extract identifying text from a row (profile name, folder name, etc.)\n * Looks for short, meaningful text that identifies the row.\n *\n * SKYR-3706: Two-pass scan over Strategies 1+2. First pass excludes\n * dynamic-looking text (UUIDs, 6+ digit ids — `_isDynamic`); second pass\n * allows them as a last resort. Without this, a row whose first cell\n * holds a backend-assigned id like \"99925484\" gets that id picked over\n * the workflow name \"WF1\" in a later cell, producing a row filter that\n * matches the recording's run only.\n */\n _getRowIdentifyingText(row) {\n var _a;\n const isAcceptable = (text, allowDynamic) => {\n if (!text) return false;\n if (text.length < 2 || text.length > 100) return false;\n if (this._isGenericText(text)) return false;\n if (!allowDynamic && this._isDynamic(text)) return false;\n return true;\n };\n for (const allowDynamic of [false, true]) {\n for (const link of row.querySelectorAll(\"a\")) {\n const text = (_a = link.textContent) == null ? void 0 : _a.trim();\n if (isAcceptable(text, allowDynamic)) {\n return text;\n }\n }\n for (const cell of row.querySelectorAll('[role=\"cell\"], [role=\"gridcell\"], td')) {\n const text = this._getDirectTextContent(cell);\n if (isAcceptable(text, allowDynamic)) {\n return text;\n }\n }\n }\n const testIdElement = row.querySelector(\"[data-testid], [data-test-id]\");\n if (testIdElement) {\n const testId = this._getTestId(testIdElement);\n if (testId && !this._isDynamic(testId) && !/^(item|row|cell|grid)/i.test(testId)) {\n return testId;\n }\n }\n const ariaLabelElement = row.querySelector('[aria-label*=\"menu for\"], [aria-label*=\"actions for\"]');\n if (ariaLabelElement) {\n const ariaLabel = ariaLabelElement.getAttribute(\"aria-label\");\n const match = ariaLabel == null ? void 0 : ariaLabel.match(/(?:menu|actions)\\s+for\\s+(.+)$/i);\n if (match && match[1]) {\n return match[1].trim();\n }\n }\n return null;\n }\n /**\n * For row containers, try to build an internal:has filter using a link child element.\n * This produces .filter({ has: getByRole(\"link\", { name: \"X\", exact: true }) })\n * which is more stable than has-text because it avoids matching against the full\n * row accessible name that may contain dynamic IDs or other volatile content.\n * Returns the JSON-encoded inner selector string for internal:has, or null.\n */\n _getRowHasFilter(container, containerSelector) {\n var _a;\n const role = container.getAttribute(\"role\");\n if (role !== \"row\" && container.tagName !== \"TR\" && !container.classList.contains(\"a-data-table__row\")) {\n return null;\n }\n const links = container.querySelectorAll(\"a\");\n for (const link of links) {\n const text = (_a = link.textContent) == null ? void 0 : _a.trim();\n if (!text || text.length < 2 || text.length > 100 || this._isGenericText(text)) {\n continue;\n }\n const escapedName = text.replace(/\\\\/g, \"\\\\\\\\\").replace(/\"/g, '\\\\\"');\n const innerSelector = `internal:role=link[name=\"${escapedName}\"s]`;\n const testSelector = `${containerSelector} >> internal:has=${JSON.stringify(innerSelector)}`;\n const verification = this._verifySelector(testSelector);\n if (verification.count === 1) {\n log(\"Has-filter: Found unique link text:\", text);\n return JSON.stringify(innerSelector);\n }\n log(\"Has-filter: Link text not unique:\", text, \"matches:\", verification.count);\n }\n return null;\n }\n /**\n * Re-anchor a positional container on its enclosing role=row when the row\n * carries a stable, non-volatile name. Only fires when the container is\n * strictly INSIDE a row (e.g. a gridcell) — row/tr containers already have\n * the has-filter and text-filter strategies.\n *\n * Anchor preference (each tried exact \"s\" first, then substring \"i\" —\n * see the comment at the match sites):\n * 1. The row's own explicit label (aria-label / aria-labelledby):\n * internal:role=row[name=\"X\"s|i] >> <relative>\n * 2. The rowheader's accessible name via has-filter (content-derived row\n * names concatenate volatile cells like modified dates):\n * [role=\"row\"] >> internal:has=\"internal:role=rowheader[name=\\\"X\\\"s|i]\" >> <relative>\n *\n * Returns the full scoped selector (verified unique) or null.\n */\n _getRowAnchorSelector(containerEl, target) {\n var _a, _b;\n let rowEl = containerEl.parentElement;\n while (rowEl && rowEl !== target.ownerDocument.body) {\n if (rowEl.getAttribute(\"role\") === \"row\" || rowEl.tagName === \"TR\") break;\n rowEl = rowEl.parentElement;\n }\n if (!rowEl || rowEl === target.ownerDocument.body) return null;\n let relative = null;\n const role = this._getRole(target);\n if (role) {\n const roleSel = `internal:role=${role}`;\n try {\n const parsed = this._injectedScript.parseSelector(roleSel);\n if (this._injectedScript.querySelectorAll(parsed, rowEl).length === 1) {\n relative = roleSel;\n }\n } catch {\n }\n }\n if (!relative) {\n relative = this._generateRelativeSelector(rowEl, target) || null;\n }\n if (!relative) {\n log(\"Row anchor: no relative selector within row\");\n return null;\n }\n const isStableAnchorName = (name) => {\n if (!name) return false;\n if (name.length < 2 || name.length > 50) return false;\n if (this._isGenericText(name)) return false;\n if (this._isDynamic(name)) return false;\n if (this._hasVolatileText(name)) return false;\n return true;\n };\n let explicitName = rowEl.getAttribute(\"aria-label\");\n if (!explicitName) {\n const labelledBy = rowEl.getAttribute(\"aria-labelledby\");\n if (labelledBy) {\n explicitName = ((_b = (_a = rowEl.ownerDocument.getElementById(labelledBy)) == null ? void 0 : _a.textContent) == null ? void 0 : _b.trim()) || null;\n }\n }\n if (isStableAnchorName(explicitName)) {\n for (const flag of [\"s\", \"i\"]) {\n const sel = `internal:role=row[name=${this._quoteCSSAttributeValue(explicitName)}${flag}] >> ${relative}`;\n if (this._verifySelector(sel).valid) {\n log(\"Row anchor: explicit row label ->\", sel);\n return sel;\n }\n }\n log(\"Row anchor: explicit row label not unique\");\n }\n const rowheader = rowEl.querySelector('[role=\"rowheader\"], th');\n if (rowheader) {\n const headerName = this._getAccessibleName(rowheader);\n if (isStableAnchorName(headerName)) {\n const rowSelector = rowEl.tagName === \"TR\" ? \"tr\" : '[role=\"row\"]';\n for (const flag of [\"s\", \"i\"]) {\n const inner = `internal:role=rowheader[name=${this._quoteCSSAttributeValue(headerName)}${flag}]`;\n const sel = `${rowSelector} >> internal:has=${JSON.stringify(inner)} >> ${relative}`;\n if (this._verifySelector(sel).valid) {\n log(\"Row anchor: rowheader has-filter ->\", sel);\n return sel;\n }\n }\n log(\"Row anchor: rowheader has-filter not unique\");\n }\n }\n return null;\n }\n /**\n * Get direct text content of an element, excluding nested elements\n */\n _getDirectTextContent(element) {\n let text = \"\";\n for (const node of element.childNodes) {\n if (node.nodeType === Node.TEXT_NODE) {\n text += node.textContent || \"\";\n }\n }\n return text.trim();\n }\n /**\n * Check if text is too generic to be a good identifier\n */\n _isGenericText(text) {\n const genericPatterns = [\n /^(edit|delete|view|open|close|save|cancel|submit|ok|yes|no)$/i,\n /^(item|row|cell|column|header|footer)$/i,\n /^(loading|please wait|...)$/i,\n /^\\d{1,5}$/,\n // Short positional numbers (e.g. row index \"1\", \"42\")\n // Longer numeric strings (6+ digits) are allowed through — they may be stable\n // identifiers (e.g. 識別コード). Uniqueness is verified downstream.\n /^[\\s\\-_]+$/\n // Just whitespace/separators\n ];\n return genericPatterns.some((p) => p.test(text));\n }\n /**\n * Escape text for use in has-text filter (handle quotes and special chars)\n */\n _escapeTextFilter(text) {\n return text.replace(/\\\\/g, \"\\\\\\\\\").replace(/\"/g, '\\\\\"');\n }\n // ==========================================================================\n // Step 4: Generate Relative Selector\n // ==========================================================================\n _generateRelativeSelector(container, target) {\n var _a;\n if (container === target || !container.contains(target)) return \"\";\n log(\"_generateRelativeSelector for:\", target.tagName, target.className);\n const svgRelatedTags = [\"svg\", \"path\", \"circle\", \"rect\", \"line\", \"polygon\", \"polyline\", \"ellipse\", \"g\", \"use\"];\n if (svgRelatedTags.includes(target.tagName.toLowerCase())) {\n log(\"Target is SVG or SVG child, walking up to find clickable ancestor\");\n const clickableAncestor = this._findClickableAncestor(container, target);\n if (clickableAncestor) {\n log(\"Found clickable ancestor:\", clickableAncestor.tagName, clickableAncestor.className);\n target = clickableAncestor;\n }\n }\n const testId = this._getTestId(target);\n if (testId && !this._isFragileTestId(testId)) {\n return this._buildTestIdSelector(target, testId);\n }\n const role = this._getRole(target);\n const ariaLabel = target.getAttribute(\"aria-label\");\n if (role && ariaLabel) {\n const explicitRole = target.getAttribute(\"role\");\n const roleSel = explicitRole ? `[role=${this._quoteCSSAttributeValue(role)}][aria-label=${this._quoteCSSAttributeValue(ariaLabel)}]` : `internal:role=${role}[name=${this._quoteCSSAttributeValue(ariaLabel)}i]`;\n try {\n const parsed = this._injectedScript.parseSelector(roleSel);\n const matches = this._injectedScript.querySelectorAll(parsed, container);\n if (matches.length === 1) {\n log(\"Strategy 2: role+aria-label ->\", roleSel);\n return roleSel;\n }\n log(\"Strategy 2: not unique in container, matches:\", matches.length);\n } catch {\n log(\"Strategy 2: selector parse failed for\", roleSel);\n }\n }\n if (role) {\n const sel = `[role=${this._quoteCSSAttributeValue(role)}]`;\n if (container.querySelectorAll(sel).length === 1) {\n return sel;\n }\n }\n if (ariaLabel) {\n const sel = `[aria-label=${this._quoteCSSAttributeValue(ariaLabel)}]`;\n if (container.querySelectorAll(sel).length === 1) {\n return sel;\n }\n }\n if (role === \"button\" || role === \"link\" || role === \"cell\" || role === \"gridcell\" || role === \"columnheader\" || role === \"rowheader\") {\n const accessibleName = this._getAccessibleName(target);\n if (accessibleName && accessibleName.length >= 2 && accessibleName.length <= 50 && !this._isGenericText(accessibleName) && !this._isDynamic(accessibleName)) {\n const roleSelector = `internal:role=${role}[name=${this._quoteCSSAttributeValue(accessibleName)}i]`;\n try {\n const parsed = this._injectedScript.parseSelector(roleSelector);\n const matches = this._injectedScript.querySelectorAll(parsed, container);\n if (matches.length === 1) {\n log(\"Strategy 4b: role with accessible name ->\", roleSelector);\n return roleSelector;\n }\n log(\"Strategy 4b: not unique in container, matches:\", matches.length);\n } catch {\n log(\"Strategy 4b: selector parse failed\");\n }\n }\n }\n if (target.tagName === \"INPUT\") {\n const allInputs = container.querySelectorAll(\"input\");\n log(\"Input strategy: found\", allInputs.length, \"inputs in container\");\n if (allInputs.length === 1) {\n log(\"Using input selector (single input in container)\");\n return \"input\";\n }\n const type = target.type || \"text\";\n const sel = `input[type=${this._quoteCSSAttributeValue(type)}]`;\n const typeMatches = container.querySelectorAll(sel);\n if (typeMatches.length === 1) {\n log(\"Using input[type] selector\");\n return sel;\n }\n log(\"Multiple inputs found, using input anyway for form container\");\n return \"input\";\n }\n if (container.tagName === \"TR\") {\n const td = target.tagName === \"TD\" ? target : target.closest(\"td\");\n if (td && container.contains(td)) {\n const interactiveAncestor = target !== td ? target.closest(\"a, button, input, select, textarea\") : null;\n const isInteractive = interactiveAncestor && td.contains(interactiveAncestor);\n if (!isInteractive) {\n const cells = container.querySelectorAll(\":scope > td\");\n const cellIndex = Array.from(cells).indexOf(td);\n if (cellIndex >= 0) {\n log(\"Strategy 5b: table cell column index ->\", `td >> nth=${cellIndex}`);\n return `td >> nth=${cellIndex}`;\n }\n } else {\n log(\"Strategy 5b: skipping, target is inside interactive element:\", interactiveAncestor.tagName);\n }\n }\n }\n const tag = target.tagName.toLowerCase();\n if (container.querySelectorAll(tag).length === 1) {\n return tag;\n }\n const targetClassList = Array.from(target.classList);\n for (const cls of targetClassList) {\n if (/^(css|styled|sc|emotion|mui)-/.test(cls)) continue;\n if (/^Mui[A-Z]/.test(cls)) continue;\n if (cls.length < 3) continue;\n const sel = `.${this._escapeCSS(cls)}`;\n if (container.querySelectorAll(sel).length === 1) {\n return sel;\n }\n }\n const textContent = (_a = target.textContent) == null ? void 0 : _a.trim();\n if (textContent && textContent.length >= 2 && textContent.length <= 100) {\n const cleanText = textContent.replace(/\\s+/g, \" \");\n if (!this._isGenericText(cleanText)) {\n const textSelector = `internal:text=\"${this._escapeTextFilter(cleanText)}\"i`;\n try {\n const parsed = this._injectedScript.parseSelector(textSelector);\n const matches = this._injectedScript.querySelectorAll(parsed, container);\n if (matches.length === 1) {\n log(\"Strategy 8: text content filter ->\", textSelector);\n return textSelector;\n }\n log(\"Strategy 8: text not unique in container, matches:\", matches.length);\n } catch {\n log(\"Strategy 8: selector parse failed\");\n }\n }\n }\n return \"\";\n }\n /**\n * For SVG child elements, walk up to find a clickable ancestor with better attributes\n * Stops at container boundary\n */\n _findClickableAncestor(container, target) {\n let current = target.parentElement;\n const presentationalRoles = [\"img\", \"presentation\", \"none\", \"graphics-symbol\"];\n while (current && current !== container && container.contains(current)) {\n const hasTestId = current.getAttribute(\"data-testid\") || current.getAttribute(\"data-test-id\");\n const role = current.getAttribute(\"role\");\n const hasInteractiveRole = role && !presentationalRoles.includes(role);\n const hasAriaLabel = current.getAttribute(\"aria-label\");\n const isClickable = [\"BUTTON\", \"A\", \"INPUT\", \"SELECT\"].includes(current.tagName);\n const hasClickHandler = current.hasAttribute(\"onclick\") || current.hasAttribute(\"data-click\");\n if (hasTestId || hasInteractiveRole || hasAriaLabel && isClickable || isClickable || hasClickHandler) {\n log(\n \"_findClickableAncestor found:\",\n current.tagName,\n \"testid:\",\n hasTestId,\n \"role:\",\n role,\n \"isClickable:\",\n isClickable\n );\n return current;\n }\n current = current.parentElement;\n }\n return null;\n }\n /**\n * Check if text contains volatile date content that changes over time\n * (modified-date columns and the like) — month-name forms (\"Apr 10, 2025\")\n * and relative words (\"Today\", \"2 days ago\"). Numeric dates and timestamps\n * are covered by _isDynamic/_hasDynamicSelector. Delegates to the shared\n * detector in volatileDate.ts so record-time scoping and selector\n * generation agree on what counts as volatile.\n */\n _hasVolatileText(text) {\n return hasVolatileDateFragment(text);\n }\n /**\n * Decide whether a data-testid is too fragile to pin as a selector part\n * (leaf relative selector or scoping container).\n *\n * `_isDynamic` alone only catches UUID / long-hex / 6+-digit values, so an\n * INDEX-suffixed testid (choice-card-1, package-name-6, invite-entry-email-0)\n * slips through and gets pinned — the \"1\"/\"6\"/\"0\" is a positional counter\n * that shifts when the list reorders or grows (SKYR-3840). `_isDynamicId`\n * already encodes the `[-_]\\d+$` index-suffix rule (plus the framework id\n * patterns) and applies equally to testids, so union the two: a testid is\n * fragile if EITHER classifier flags it. This mirrors the top-level\n * `_hasDynamicSelector` testid rule, which already rejects `[-_]\\d+`.\n */\n _isFragileTestId(testId) {\n return this._isDynamic(testId) || this._isDynamicId(testId);\n }\n _isDynamic(value) {\n if (/^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i.test(value)) return true;\n if (/[-_][0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i.test(value)) return true;\n const hexSuffix = value.match(/[-_]([0-9a-f]{8,})$/i);\n if (hexSuffix && /[0-9]/.test(hexSuffix[1])) return true;\n if (/^\\d{6,}$/.test(value)) return true;\n if (/^\\d{10,13}$/.test(value)) return true;\n return false;\n }\n _getRole(element) {\n var _a;\n const explicit = element.getAttribute(\"role\");\n if (explicit) return explicit;\n const tag = element.tagName.toLowerCase();\n const roles = {\n button: \"button\",\n a: \"link\",\n select: \"combobox\",\n textarea: \"textbox\",\n img: \"img\",\n tr: \"row\",\n // Headings carry the implicit ARIA role 'heading'. Clickable card/tile\n // widgets often use a roleless <h5> that holds a stable label (the card\n // title) — without this mapping _getRole returns null and the role+name\n // anchor strategies (relative 4b, alternative 5b) never fire, so the\n // recorder falls back to the card's index-suffixed testid (SKYR-3840).\n h1: \"heading\",\n h2: \"heading\",\n h3: \"heading\",\n h4: \"heading\",\n h5: \"heading\",\n h6: \"heading\"\n };\n if (tag === \"input\") {\n const type = ((_a = element.type) == null ? void 0 : _a.toLowerCase()) || \"text\";\n const inputRoles = {\n checkbox: \"checkbox\",\n radio: \"radio\",\n button: \"button\",\n submit: \"button\"\n };\n return inputRoles[type] || \"textbox\";\n }\n if (tag === \"td\") {\n const table = element.closest(\"table\");\n const tableRole = table == null ? void 0 : table.getAttribute(\"role\");\n return tableRole === \"grid\" || tableRole === \"treegrid\" ? \"gridcell\" : \"cell\";\n }\n if (tag === \"th\")\n return getAriaRole(element);\n return roles[tag] || null;\n }\n // ==========================================================================\n // Step 5: Verify Selector\n // ==========================================================================\n _verifySelector(selector) {\n try {\n const parsed = this._injectedScript.parseSelector(selector);\n const elements = this._injectedScript.querySelectorAll(parsed, this._injectedScript.document);\n return {\n valid: elements.length === 1,\n count: elements.length,\n elements: Array.from(elements)\n };\n } catch {\n return { valid: false, count: 0, elements: [] };\n }\n }\n // ==========================================================================\n // Alternative Selector Generation (Fallback for Dynamic IDs without Containers)\n // ==========================================================================\n /**\n * Generate an alternative stable selector when:\n * 1. The original selector contains dynamic/unstable IDs (e.g., #mui-6, #react-aria123)\n * 2. No scoping container was found (element is not in a repeating context)\n *\n * This handles cases like MUI form inputs that have auto-generated IDs but\n * also have stable attributes like name, aria-label, or placeholder.\n *\n * Priority order for form elements:\n * 1. input[name=\"...\"] - Most stable for form elements\n * 2. [aria-label=\"...\"] - Accessible and stable\n * 3. [placeholder=\"...\"] - Common for inputs\n * 4. input[type=\"...\"] - If unique on page\n */\n _generateAlternativeSelector(element, originalSelector) {\n var _a;\n log(\"_generateAlternativeSelector for:\", element.tagName, \"original:\", originalSelector);\n const tag = element.tagName.toUpperCase();\n let alternativeSelector = null;\n if (tag === \"INPUT\" || tag === \"TEXTAREA\" || tag === \"SELECT\") {\n const name = element.getAttribute(\"name\");\n if (name && !this._isDynamic(name)) {\n alternativeSelector = `${tag.toLowerCase()}[name=${this._quoteCSSAttributeValue(name)}]`;\n log(\"Alternative strategy 1: name attribute ->\", alternativeSelector);\n }\n }\n if (!alternativeSelector) {\n const ariaLabel = element.getAttribute(\"aria-label\");\n if (ariaLabel && ariaLabel.length >= 2 && ariaLabel.length <= 100) {\n alternativeSelector = `[aria-label=${this._quoteCSSAttributeValue(ariaLabel)}]`;\n log(\"Alternative strategy 2: aria-label ->\", alternativeSelector);\n }\n }\n if (!alternativeSelector && (tag === \"INPUT\" || tag === \"TEXTAREA\")) {\n const placeholder = element.placeholder;\n if (placeholder && placeholder.length >= 2 && placeholder.length <= 100) {\n alternativeSelector = `${tag.toLowerCase()}[placeholder=${this._quoteCSSAttributeValue(placeholder)}]`;\n log(\"Alternative strategy 3: placeholder ->\", alternativeSelector);\n }\n }\n if (!alternativeSelector && tag === \"INPUT\") {\n const type = element.type || \"text\";\n const specificTypes = [\"email\", \"password\", \"tel\", \"url\", \"search\", \"number\", \"date\", \"time\", \"datetime-local\", \"month\", \"week\", \"color\", \"file\"];\n if (specificTypes.includes(type)) {\n const typeSelector = `input[type=${this._quoteCSSAttributeValue(type)}]`;\n const verification2 = this._verifySelector(typeSelector);\n if (verification2.valid) {\n alternativeSelector = typeSelector;\n log(\"Alternative strategy 4: unique input type ->\", alternativeSelector);\n }\n }\n }\n if (!alternativeSelector && (tag === \"BUTTON\" || tag === \"INPUT\" && element.type === \"submit\")) {\n const buttonText = (_a = element.textContent) == null ? void 0 : _a.trim();\n if (buttonText && buttonText.length >= 2 && buttonText.length <= 50 && !this._isGenericText(buttonText)) {\n alternativeSelector = `internal:role=button[name=${this._quoteCSSAttributeValue(buttonText)}i]`;\n log(\"Alternative strategy 5: button text ->\", alternativeSelector);\n }\n }\n if (!alternativeSelector) {\n const role = this._getRole(element);\n const accessibleName = role ? this._getAccessibleName(element) : null;\n if (role && accessibleName && accessibleName.length >= 2 && accessibleName.length <= 50 && !this._isGenericText(accessibleName) && !this._isDynamic(accessibleName)) {\n alternativeSelector = `internal:role=${role}[name=${this._quoteCSSAttributeValue(accessibleName)}i]`;\n log(\"Alternative strategy 5b: role+name ->\", alternativeSelector);\n }\n }\n if (!alternativeSelector) {\n const title = element.getAttribute(\"title\");\n if (title && title.length >= 2 && title.length <= 100) {\n alternativeSelector = `[title=${this._quoteCSSAttributeValue(title)}]`;\n log(\"Alternative strategy 6: title ->\", alternativeSelector);\n }\n }\n if (!alternativeSelector) {\n log(\"No alternative selector found\");\n return null;\n }\n const verification = this._verifySelector(alternativeSelector);\n log(\"Alternative verification:\", verification.valid ? \"PASS\" : \"FAIL\", \"| Matches:\", verification.count);\n if (!verification.valid) {\n if (verification.count > 1 && tag) {\n const taggedSelector = `${tag.toLowerCase()}${alternativeSelector.startsWith(\"[\") ? alternativeSelector : \" \" + alternativeSelector}`;\n const taggedVerification = this._verifySelector(taggedSelector);\n if (taggedVerification.valid) {\n alternativeSelector = taggedSelector;\n log(\"Made unique by adding tag:\", alternativeSelector);\n } else {\n log(\"Alternative selector not unique, rejecting\");\n return null;\n }\n } else {\n log(\"Alternative selector not unique, rejecting\");\n return null;\n }\n }\n const result = {\n selector: alternativeSelector,\n container: null,\n elements: verification.elements,\n strategy: \"alternative\",\n description: `Alternative selector for dynamic ID: ${originalSelector} -> ${alternativeSelector}`,\n containerSelector: \"\",\n containerIndex: -1,\n relativeSelector: \"\",\n isAlternativeSelector: true\n };\n logTable({\n \"Original (unstable)\": originalSelector,\n \"Alternative (stable)\": alternativeSelector,\n \"Strategy\": \"alternative\",\n \"Reason\": \"Dynamic ID without container\"\n });\n return result;\n }\n // ==========================================================================\n // Link-Based Selector Generation (for non-unique links)\n // ==========================================================================\n /**\n * Try to generate a simple link-based selector for navigation elements.\n * This is preferred over container-based scoping for links because:\n * 1. href attributes are stable (tied to routing)\n * 2. Accessible names are semantic and stable\n *\n * Checks the element and its ancestors (up to 3 levels) for link elements.\n *\n * Strategy 7: a[href=\"...\"] - Most stable for navigation\n * Strategy 8: getByRole('link', { name: '...' }) - Semantic and accessible\n */\n _tryLinkSelector(element, originalSelector) {\n log(\"_tryLinkSelector checking:\", element.tagName);\n const linkElement = this._findLinkElement(element);\n if (!linkElement) {\n log(\"No link element found\");\n return null;\n }\n log(\"Found link element:\", linkElement.tagName, \"href:\", linkElement.getAttribute(\"href\"));\n let linkSelector = null;\n const href = linkElement.getAttribute(\"href\");\n if (href && this._isStableHref(href)) {\n const hrefSelector = `a[href=${this._quoteCSSAttributeValue(href)}]`;\n const verification2 = this._verifySelector(hrefSelector);\n if (verification2.valid) {\n linkSelector = hrefSelector;\n log(\"Strategy 7: href selector ->\", linkSelector);\n } else {\n log(\"Strategy 7: href not unique, matches:\", verification2.count);\n }\n }\n if (!linkSelector) {\n const accessibleName = this._getAccessibleName(linkElement);\n if (accessibleName && accessibleName.length >= 2 && accessibleName.length <= 50) {\n const roleSelector = `internal:role=link[name=${this._quoteCSSAttributeValue(accessibleName)}i]`;\n const verification2 = this._verifySelector(roleSelector);\n if (verification2.valid) {\n linkSelector = roleSelector;\n log(\"Strategy 8: role=link with name ->\", linkSelector);\n } else {\n log(\"Strategy 8: role=link not unique, matches:\", verification2.count);\n if (verification2.count > 1) {\n const exactRoleSelector = `internal:role=link[name=${this._quoteCSSAttributeValue(accessibleName)}]`;\n const exactVerification = this._verifySelector(exactRoleSelector);\n if (exactVerification.valid) {\n linkSelector = exactRoleSelector;\n log(\"Strategy 8b: role=link with exact name ->\", linkSelector);\n }\n }\n }\n }\n }\n if (!linkSelector) {\n log(\"No suitable link selector found\");\n return null;\n }\n const verification = this._verifySelector(linkSelector);\n const result = {\n selector: linkSelector,\n container: null,\n elements: verification.elements,\n strategy: \"alternative\",\n description: `Link selector: ${originalSelector} -> ${linkSelector}`,\n containerSelector: \"\",\n containerIndex: -1,\n relativeSelector: \"\",\n isAlternativeSelector: true\n };\n logTable({\n \"Original\": originalSelector,\n \"Link selector\": linkSelector,\n \"Strategy\": \"link-based (7/8)\",\n \"Element\": linkElement.tagName\n });\n return result;\n }\n /**\n * Find the link element - either the element itself or an ancestor (up to 3 levels)\n * Returns the <a> tag or element with role=\"link\"\n */\n _findLinkElement(element) {\n let current = element;\n let depth = 0;\n const maxDepth = 3;\n while (current && depth <= maxDepth) {\n if (current.tagName === \"A\") {\n return current;\n }\n if (current.getAttribute(\"role\") === \"link\") {\n }\n current = current.parentElement;\n depth++;\n }\n return null;\n }\n /**\n * Check if href is stable (not dynamic/session-specific)\n */\n _isStableHref(href) {\n if (!href || href === \"#\" || href.startsWith(\"javascript:\")) {\n return false;\n }\n if (this._isDynamic(href)) {\n return false;\n }\n if (/[a-f0-9]{32,}/i.test(href)) {\n return false;\n }\n if (/\\/\\d{6,}(\\/|$)/.test(href)) {\n return false;\n }\n return true;\n }\n /**\n * Get the accessible name of an element.\n * This follows a simplified version of the accessible name computation:\n * 1. aria-label attribute\n * 2. aria-labelledby (resolve to referenced element's text)\n * 3. Text content (for links, buttons)\n */\n _getAccessibleName(element) {\n var _a, _b;\n const ariaLabel = element.getAttribute(\"aria-label\");\n if (ariaLabel && ariaLabel.trim()) {\n return ariaLabel.trim();\n }\n const labelledBy = element.getAttribute(\"aria-labelledby\");\n if (labelledBy) {\n const labelElement = element.ownerDocument.getElementById(labelledBy);\n if (labelElement) {\n const labelText = (_a = labelElement.textContent) == null ? void 0 : _a.trim();\n if (labelText) {\n return labelText;\n }\n }\n }\n const textContent = (_b = element.textContent) == null ? void 0 : _b.trim();\n if (textContent && textContent.length <= 100) {\n return textContent.replace(/\\s+/g, \" \");\n }\n return null;\n }\n};\nfunction escapeCSS(value) {\n if (typeof CSS !== \"undefined\" && CSS.escape) {\n return CSS.escape(value);\n }\n return value.replace(/([!\"#$%&'()*+,.\\/:;<=>?@[\\\\\\]^`{|}~])/g, \"\\\\$1\");\n}\nfunction quoteCSSAttributeValue(text) {\n return `\"${text.replace(/[\"\\\\]/g, (char) => \"\\\\\" + char)}\"`;\n}\nfunction applyScopingHook(injectedScript, element, selector, elements) {\n const scoping = new ScopingHandler(injectedScript);\n return scoping.applyScopingHook(element, selector, elements);\n}\n\n// packages/injected/src/recorder/skyramp/nestedElementHandler.ts\nvar NestedElementHandler = class {\n constructor(document2) {\n this._enabled = false;\n this._savedButtonRoles = /* @__PURE__ */ new Map();\n this._hiddenDuplicateButtons = [];\n this._document = document2;\n }\n get enabled() {\n return this._enabled;\n }\n toggle() {\n this._enabled = !this._enabled;\n if (this._enabled) {\n this._enableNestedElementAccess();\n } else {\n this._disableNestedElementAccess();\n }\n }\n _enableNestedElementAccess() {\n const buttonsWithRole = this._document.querySelectorAll('[role=\"button\"]');\n buttonsWithRole.forEach((element) => {\n const role = element.getAttribute(\"role\");\n if (element.children.length > 0 && role) {\n this._savedButtonRoles.set(element, role);\n element.removeAttribute(\"role\");\n const nestedButtons = element.querySelectorAll(\"button\");\n nestedButtons.forEach((nestedButton) => {\n this._createDuplicateButton(nestedButton, element);\n });\n }\n });\n }\n _disableNestedElementAccess() {\n this._savedButtonRoles.forEach((role, element) => {\n element.setAttribute(\"role\", role);\n });\n this._savedButtonRoles.clear();\n this._hiddenDuplicateButtons.forEach((button) => {\n button.remove();\n });\n this._hiddenDuplicateButtons = [];\n }\n _createDuplicateButton(nestedButton, wrapperElement) {\n var _a;\n const ariaLabel = nestedButton.getAttribute(\"aria-label\");\n const textContent = (_a = nestedButton.textContent) == null ? void 0 : _a.trim();\n const accessibleName = ariaLabel || textContent;\n if (!accessibleName)\n return;\n const duplicate = this._document.createElement(\"button\");\n duplicate.textContent = (textContent || \"\") + \" dup\";\n if (ariaLabel)\n duplicate.setAttribute(\"aria-label\", ariaLabel + \" dup\");\n duplicate.style.cssText = \"position: absolute !important; left: -9999px !important; width: 1px !important; height: 1px !important; overflow: hidden !important; pointer-events: none !important;\";\n duplicate.setAttribute(\"tabindex\", \"-1\");\n duplicate.setAttribute(\"data-pw-nested-button-duplicate\", \"true\");\n duplicate.disabled = true;\n wrapperElement.appendChild(duplicate);\n this._hiddenDuplicateButtons.push(duplicate);\n }\n /**\n * Handle a click on a nested element within a checkbox/radio tile.\n * Returns the scoped selector and auto-disable flag, or null if not a nested click.\n */\n handleNestedClick(clickedElement, hoveredModel, injectedScript, testIdAttributeName) {\n let parentElement = null;\n if (hoveredModel.elements && hoveredModel.elements.length > 0) {\n parentElement = hoveredModel.elements[0];\n }\n const isChildClick = parentElement && clickedElement !== parentElement && parentElement.contains(clickedElement);\n if (!isChildClick) {\n if (this._isInsideButtonWrapperWithNativeInput(clickedElement)) {\n return {\n targetSelector: hoveredModel.selector,\n shouldAutoDisable: true\n };\n }\n return null;\n }\n const parentRole = parentElement.getAttribute(\"role\");\n if (parentRole !== \"checkbox\" && parentRole !== \"radio\") {\n if (!this._isInsideButtonWrapperWithNativeInput(parentElement)) {\n return null;\n }\n }\n const generated = injectedScript.generateSelector(clickedElement, {\n testIdAttributeName\n });\n let targetSelector;\n if (generated.selector === hoveredModel.selector) {\n const childSelector = this._buildChildSelector(clickedElement, parentElement, testIdAttributeName);\n if (!childSelector) {\n return null;\n }\n targetSelector = `${hoveredModel.selector} >> ${childSelector}`;\n } else {\n const parentSelector = hoveredModel.selector;\n const childSelector = generated.selector;\n if (parentSelector && childSelector && !childSelector.startsWith(parentSelector) && !childSelector.includes(\">>\")) {\n targetSelector = `${parentSelector} >> ${childSelector}`;\n } else {\n targetSelector = childSelector;\n }\n }\n return {\n targetSelector,\n shouldAutoDisable: true\n };\n }\n /**\n * Build a CSS selector for a child element within a parent container.\n * Handles SVG elements and their children specially.\n */\n _buildChildSelector(clickedElement, parentElement, testIdAttr) {\n const isSvgElement = (el) => el.tagName.toLowerCase() === \"svg\";\n const isSvgChild = (el) => {\n const tag = el.tagName.toLowerCase();\n return tag === \"path\" || tag === \"g\" || tag === \"circle\" || tag === \"rect\" || tag === \"polygon\" || tag === \"line\" || tag === \"polyline\" || tag === \"ellipse\";\n };\n let targetElement = clickedElement;\n if (isSvgChild(clickedElement)) {\n let parent = clickedElement.parentElement;\n while (parent && parent !== parentElement) {\n if (isSvgElement(parent) && parent.classList.length > 0) {\n const classes = Array.from(parent.classList);\n if (classes.some((c) => c.includes(\"chevron\") || c.includes(\"icon\") || c.includes(\"expandable\"))) {\n targetElement = parent;\n break;\n }\n }\n if (!isSvgElement(parent) && !isSvgChild(parent) && parent.classList.length > 0) {\n targetElement = parent;\n break;\n }\n parent = parent.parentElement;\n }\n } else if (isSvgElement(clickedElement) && clickedElement.classList.length === 0) {\n let parent = clickedElement.parentElement;\n while (parent && parent !== parentElement) {\n if (parent.classList.length > 0) {\n targetElement = parent;\n break;\n }\n parent = parent.parentElement;\n }\n }\n if (targetElement.hasAttribute(testIdAttr)) {\n const attrValue = targetElement.getAttribute(testIdAttr) || \"\";\n return `[${testIdAttr}=${quoteCSSAttributeValue(attrValue)}]`;\n }\n if (targetElement.classList.length > 0) {\n const classes = Array.from(targetElement.classList);\n const meaningfulClasses = classes.filter(\n (c) => c.includes(\"expand\") || c.includes(\"chevron\") || c.includes(\"badge\") || c.includes(\"checkmark\") || c.includes(\"heading\") || c.includes(\"status\") || c.includes(\"icon\") || c.includes(\"button\") || c.includes(\"title\")\n );\n if (meaningfulClasses.length > 0) {\n const specificClass = meaningfulClasses.find(\n (c) => c.includes(\"chevron\") || c.includes(\"checkmark\") || c.includes(\"badge\")\n ) || meaningfulClasses[0];\n return \".\" + escapeCSS(specificClass);\n } else if (classes.length > 0) {\n return \".\" + escapeCSS(classes[0]);\n }\n }\n return targetElement.tagName.toLowerCase();\n }\n /**\n * Check if an element is inside a former role=\"button\" wrapper (stripped by\n * _enableNestedElementAccess) that also contains a native checkbox or radio input.\n */\n _isInsideButtonWrapperWithNativeInput(element) {\n let ancestor = element;\n while (ancestor) {\n if (this._savedButtonRoles.has(ancestor)) {\n return !!ancestor.querySelector('input[type=\"checkbox\"], input[type=\"radio\"]');\n }\n ancestor = ancestor.parentElement;\n }\n return false;\n }\n cleanup() {\n if (this._enabled) {\n this._disableNestedElementAccess();\n this._enabled = false;\n }\n }\n};\n\n// packages/injected/src/recorder/skyramp/pdfJsViewer.ts\nvar PdfJsViewer = class _PdfJsViewer {\n constructor(container) {\n this._pdfDoc = null;\n this._canvasElements = [];\n this._thumbnailElements = [];\n this._isRendering = false;\n this._toolbar = null;\n this._sidebar = null;\n this._mainContent = null;\n this._currentPage = 1;\n this._pageDisplay = null;\n this._zoomDisplay = null;\n this._currentZoom = 1;\n // 100%\n this._rotation = 0;\n // 0, 90, 180, 270 degrees\n this._pdfDataUrl = \"\";\n // Store for download\n this._filename = \"Document.pdf\";\n this._moreMenuElement = null;\n this._moreMenuCleanup = null;\n this._twoPageView = false;\n this._annotationsVisible = true;\n this._container = container;\n }\n /**\n * Loads PDF.js library from CDN if not already loaded\n */\n static async loadPdfJs() {\n if (window.pdfjsLib) {\n return;\n }\n console.log(\"[PDF.js] Loading PDF.js library from CDN...\");\n const script = document.createElement(\"script\");\n script.src = \"https://cdnjs.cloudflare.com/ajax/libs/pdf.js/3.11.174/pdf.min.js\";\n return new Promise((resolve, reject) => {\n script.onload = () => {\n if (!window.pdfjsLib) {\n reject(new Error(\"PDF.js loaded but pdfjsLib not available\"));\n return;\n }\n window.pdfjsLib.GlobalWorkerOptions.workerSrc = \"https://cdnjs.cloudflare.com/ajax/libs/pdf.js/3.11.174/pdf.worker.min.js\";\n console.log(\"[PDF.js] \\u2705 PDF.js library loaded successfully\");\n resolve();\n };\n script.onerror = () => reject(new Error(\"Failed to load PDF.js from CDN\"));\n document.head.appendChild(script);\n });\n }\n /**\n * Renders a PDF from a data URL\n */\n async renderPdf(config) {\n const { pdfDataUrl, filename, onReady, onError } = config;\n try {\n this._pdfDataUrl = pdfDataUrl;\n this._filename = filename || \"Document.pdf\";\n await _PdfJsViewer.loadPdfJs();\n console.log(\"[PDF.js] Rendering PDF...\");\n this._isRendering = true;\n const loadingTask = window.pdfjsLib.getDocument(pdfDataUrl);\n this._pdfDoc = await loadingTask.promise;\n console.log(`[PDF.js] PDF loaded: ${this._pdfDoc.numPages} pages`);\n if (document.documentElement) {\n document.documentElement.style.height = \"auto\";\n }\n if (document.body) {\n document.body.style.margin = \"0\";\n document.body.style.padding = \"0\";\n document.body.style.width = \"100%\";\n document.body.style.height = \"auto\";\n document.body.style.overflow = \"auto\";\n }\n this._container.innerHTML = \"\";\n this._canvasElements = [];\n this._thumbnailElements = [];\n this._container.style.cssText = `\n width: 100%;\n min-height: 100vh;\n display: flex;\n flex-direction: column;\n background-color: #525252;\n margin: 0;\n padding: 0;\n `;\n this._toolbar = document.createElement(\"div\");\n this._toolbar.style.cssText = `\n width: 100%;\n height: 56px;\n background-color: #4a4a4a;\n color: #e8eaed;\n display: flex;\n align-items: center;\n justify-content: space-between;\n padding: 0 8px;\n box-sizing: border-box;\n font-family: 'Roboto', Arial, sans-serif;\n font-size: 14px;\n flex-shrink: 0;\n border-bottom: 1px solid #2a2a2a;\n position: sticky;\n top: 0;\n z-index: 10;\n `;\n const leftSection = document.createElement(\"div\");\n leftSection.style.cssText = \"display: flex; align-items: center; gap: 12px;\";\n const menuBtn = this._createToolbarButton(\"\\u2261\", \"Menu\", () => {\n if (this._sidebar) {\n const isHidden = this._sidebar.style.display === \"none\";\n this._sidebar.style.display = isHidden ? \"block\" : \"none\";\n }\n });\n menuBtn.style.fontSize = \"24px\";\n leftSection.appendChild(menuBtn);\n const filenameDisplay = document.createElement(\"div\");\n filenameDisplay.textContent = filename || \"Document.pdf\";\n filenameDisplay.style.cssText = `\n color: #e8eaed;\n font-size: 14px;\n font-weight: 400;\n margin-left: 4px;\n `;\n leftSection.appendChild(filenameDisplay);\n const centerSection = document.createElement(\"div\");\n centerSection.style.cssText = \"display: flex; align-items: center; gap: 12px;\";\n const pageNav = document.createElement(\"div\");\n pageNav.style.cssText = \"display: flex; align-items: center; gap: 8px;\";\n const pageDisplay = document.createElement(\"span\");\n pageDisplay.textContent = `1 / ${this._pdfDoc.numPages}`;\n pageDisplay.style.cssText = \"color: #e8eaed; font-size: 13px; min-width: 50px; text-align: center;\";\n pageNav.appendChild(pageDisplay);\n centerSection.appendChild(pageNav);\n const divider1 = document.createElement(\"div\");\n divider1.style.cssText = \"width: 1px; height: 24px; background-color: #5f5f5f;\";\n centerSection.appendChild(divider1);\n const zoomControls = document.createElement(\"div\");\n zoomControls.style.cssText = \"display: flex; align-items: center; gap: 8px;\";\n const zoomOutBtn = this._createToolbarButton(\"\\u2212\", \"Zoom out\", () => {\n this._zoom(this._currentZoom - 0.1);\n });\n zoomControls.appendChild(zoomOutBtn);\n const zoomDisplay = document.createElement(\"span\");\n zoomDisplay.textContent = \"100%\";\n zoomDisplay.style.cssText = \"color: #e8eaed; font-size: 13px; min-width: 45px; text-align: center; cursor: pointer;\";\n zoomDisplay.title = \"Reset zoom to 100%\";\n zoomDisplay.addEventListener(\"click\", () => {\n this._zoom(1);\n });\n zoomControls.appendChild(zoomDisplay);\n const zoomInBtn = this._createToolbarButton(\"+\", \"Zoom in\", () => {\n this._zoom(this._currentZoom + 0.1);\n });\n zoomControls.appendChild(zoomInBtn);\n centerSection.appendChild(zoomControls);\n const divider2 = document.createElement(\"div\");\n divider2.style.cssText = \"width: 1px; height: 24px; background-color: #5f5f5f;\";\n centerSection.appendChild(divider2);\n const fitBtn = this._createToolbarButton(\"\\u22A1\", \"Fit to page\", () => {\n this._fitToPage();\n });\n fitBtn.style.fontSize = \"18px\";\n centerSection.appendChild(fitBtn);\n const rotateBtn = this._createToolbarButton(\"\\u21BB\", \"Rotate clockwise\", () => {\n this._rotate();\n });\n rotateBtn.style.fontSize = \"18px\";\n centerSection.appendChild(rotateBtn);\n const rightSection = document.createElement(\"div\");\n rightSection.style.cssText = \"display: flex; align-items: center; gap: 8px;\";\n const downloadBtn = this._createToolbarButton(\"\\u2B07\", \"Download\", () => {\n this._download();\n });\n downloadBtn.style.fontSize = \"18px\";\n rightSection.appendChild(downloadBtn);\n const printBtn = this._createToolbarButton(\"\\u{1F5A8}\", \"Print\", () => {\n this._print();\n });\n printBtn.style.fontSize = \"16px\";\n rightSection.appendChild(printBtn);\n let moreBtn;\n moreBtn = this._createToolbarButton(\"\\u22EE\", \"More options\", () => {\n this._toggleMoreMenu(moreBtn);\n });\n moreBtn.style.fontSize = \"20px\";\n rightSection.appendChild(moreBtn);\n this._toolbar.appendChild(leftSection);\n this._toolbar.appendChild(centerSection);\n this._toolbar.appendChild(rightSection);\n this._pageDisplay = pageDisplay;\n this._zoomDisplay = zoomDisplay;\n const contentWrapper = document.createElement(\"div\");\n contentWrapper.style.cssText = `\n width: 100%;\n flex: 1;\n display: flex;\n `;\n this._sidebar = document.createElement(\"div\");\n this._sidebar.style.cssText = `\n width: 294px;\n height: calc(100vh - 56px);\n overflow-y: auto;\n overflow-x: hidden;\n background-color: #3f3f3f;\n border-right: 1px solid #2a2a2a;\n padding: 20px 35px 20px 70px;\n box-sizing: border-box;\n flex-shrink: 0;\n position: sticky;\n top: 56px;\n align-self: flex-start;\n `;\n this._mainContent = document.createElement(\"div\");\n this._mainContent.style.cssText = `\n flex: 1;\n overflow: visible;\n background-color: #525252;\n position: relative;\n padding: 0;\n box-sizing: border-box;\n `;\n this._container.appendChild(this._toolbar);\n contentWrapper.appendChild(this._sidebar);\n contentWrapper.appendChild(this._mainContent);\n this._container.appendChild(contentWrapper);\n for (let pageNum = 1; pageNum <= this._pdfDoc.numPages; pageNum++) {\n await this._renderPage(pageNum);\n await this._renderThumbnail(pageNum);\n }\n this._setupScrollSync();\n this._isRendering = false;\n console.log(\"[PDF.js] \\u2705 All pages rendered successfully\");\n if (onReady) {\n onReady();\n }\n } catch (error) {\n this._isRendering = false;\n console.error(\"[PDF.js] \\u274C Failed to render PDF:\", error);\n if (onError) {\n onError(error);\n }\n }\n }\n /**\n * Injects the minimal CSS required by PDF.js renderTextLayer (once per document).\n * PDF.js relies on external CSS for `position: absolute` and `transform-origin` on\n * text layer spans — without it the spans are in normal flow and misaligned.\n */\n static _injectTextLayerCss() {\n if (document.getElementById(\"pw-pdf-text-layer-styles\"))\n return;\n const style = document.createElement(\"style\");\n style.id = \"pw-pdf-text-layer-styles\";\n style.textContent = `\n div[data-pw-pdf-text-layer] {\n line-height: 1;\n -webkit-text-size-adjust: none;\n -moz-text-size-adjust: none;\n text-size-adjust: none;\n forced-color-adjust: none;\n transform-origin: 0 0;\n }\n div[data-pw-pdf-text-layer] :is(span, br) {\n color: transparent;\n position: absolute;\n white-space: pre;\n cursor: text;\n transform-origin: 0% 0%;\n pointer-events: none;\n }\n div[data-pw-pdf-text-layer] span.markedContent {\n top: 0;\n height: 0;\n }\n `;\n document.head.appendChild(style);\n }\n /**\n * Renders a single page in the main content area\n */\n async _renderPage(pageNum) {\n if (!this._mainContent) return;\n const page = await this._pdfDoc.getPage(pageNum);\n const viewport = page.getViewport({ scale: 1 });\n const containerWidth = this._mainContent.clientWidth || 800;\n let scale;\n let wrapperMargin;\n if (this._twoPageView) {\n const availableWidth = (containerWidth / 2 - 32) * 0.95;\n scale = availableWidth / viewport.width * this._currentZoom;\n wrapperMargin = \"16px auto\";\n } else {\n const availableWidth = (containerWidth - 80) * 0.89;\n scale = availableWidth / viewport.width * this._currentZoom;\n wrapperMargin = \"16px 20px 16px 80px\";\n }\n const scaledViewport = page.getViewport({ scale });\n const canvasWrapper = document.createElement(\"div\");\n canvasWrapper.setAttribute(\"data-page-number\", pageNum.toString());\n canvasWrapper.style.cssText = `\n position: relative;\n margin: ${wrapperMargin};\n background: white;\n box-shadow: 0 1px 3px rgba(0, 0, 0, 0.3), 0 4px 8px rgba(0, 0, 0, 0.15);\n width: ${scaledViewport.width}px;\n height: ${scaledViewport.height}px;\n box-sizing: border-box;\n `;\n const canvas = document.createElement(\"canvas\");\n canvas.width = scaledViewport.width;\n canvas.height = scaledViewport.height;\n canvas.style.cssText = `\n display: block;\n width: 100%;\n height: 100%;\n `;\n canvasWrapper.appendChild(canvas);\n this._mainContent.appendChild(canvasWrapper);\n this._canvasElements.push(canvas);\n const context = canvas.getContext(\"2d\");\n if (!context) {\n throw new Error(\"Failed to get canvas 2D context\");\n }\n const renderContext = {\n canvasContext: context,\n viewport: scaledViewport\n };\n await page.render(renderContext).promise;\n try {\n _PdfJsViewer._injectTextLayerCss();\n const textContent = await page.getTextContent();\n const textLayer = document.createElement(\"div\");\n textLayer.setAttribute(\"data-pw-pdf-text-layer\", pageNum.toString());\n textLayer.style.cssText = `\n position: absolute;\n top: 0;\n left: 0;\n width: ${scaledViewport.width}px;\n height: ${scaledViewport.height}px;\n overflow: hidden;\n opacity: 0;\n `;\n textLayer.style.setProperty(\"--scale-factor\", String(scale));\n canvasWrapper.appendChild(textLayer);\n const renderTask = window.pdfjsLib.renderTextLayer({\n textContentSource: textContent,\n container: textLayer,\n viewport: scaledViewport,\n textDivs: []\n });\n await renderTask.promise;\n } catch (e) {\n console.warn(`[PDF.js] Text layer render failed for page ${pageNum}:`, e);\n }\n console.log(`[PDF.js] Rendered page ${pageNum}/${this._pdfDoc.numPages}`);\n }\n /**\n * Renders a thumbnail for the sidebar\n */\n async _renderThumbnail(pageNum) {\n if (!this._sidebar) return;\n const page = await this._pdfDoc.getPage(pageNum);\n const viewport = page.getViewport({ scale: 1 });\n const thumbnailWidth = 118;\n const scale = thumbnailWidth / viewport.width;\n const scaledViewport = page.getViewport({ scale });\n const thumbWrapper = document.createElement(\"div\");\n thumbWrapper.setAttribute(\"data-page-number\", pageNum.toString());\n thumbWrapper.style.cssText = `\n margin: 18px auto;\n background: white;\n cursor: pointer;\n border: 3px solid transparent;\n box-sizing: border-box;\n transition: border-color 0.15s;\n width: fit-content;\n box-shadow: 0 1px 3px rgba(0, 0, 0, 0.2);\n `;\n if (pageNum === 1) {\n thumbWrapper.style.borderColor = \"#1a73e8\";\n }\n const canvas = document.createElement(\"canvas\");\n canvas.width = scaledViewport.width;\n canvas.height = scaledViewport.height;\n canvas.style.cssText = \"display: block; width: 100%; height: auto;\";\n const label = document.createElement(\"div\");\n label.textContent = pageNum.toString();\n label.style.cssText = `\n text-align: center;\n color: #dadce0;\n font-size: 13px;\n padding: 5px;\n background: #3f3f3f;\n font-family: 'Roboto', Arial, sans-serif;\n `;\n thumbWrapper.appendChild(canvas);\n thumbWrapper.appendChild(label);\n this._sidebar.appendChild(thumbWrapper);\n this._thumbnailElements.push(thumbWrapper);\n thumbWrapper.addEventListener(\"click\", () => {\n this._scrollToPage(pageNum);\n });\n const context = canvas.getContext(\"2d\");\n if (!context) return;\n await page.render({\n canvasContext: context,\n viewport: scaledViewport\n }).promise;\n }\n /**\n * Scrolls to a specific page\n */\n _scrollToPage(pageNum) {\n if (!this._mainContent) return;\n const pageElement = this._mainContent.querySelector(`[data-page-number=\"${pageNum}\"]`);\n if (pageElement) {\n const rect = pageElement.getBoundingClientRect();\n window.scrollBy({ top: rect.top - 56, behavior: \"smooth\" });\n this._updateCurrentPage(pageNum);\n }\n }\n /**\n * Updates the current page highlight in sidebar (match Chrome's blue highlight)\n */\n _updateCurrentPage(pageNum) {\n if (this._currentPage === pageNum) return;\n if (this._thumbnailElements[this._currentPage - 1]) {\n this._thumbnailElements[this._currentPage - 1].style.borderColor = \"transparent\";\n }\n if (this._thumbnailElements[pageNum - 1]) {\n this._thumbnailElements[pageNum - 1].style.borderColor = \"#1a73e8\";\n }\n this._currentPage = pageNum;\n if (this._pageDisplay) {\n this._pageDisplay.textContent = `${pageNum} / ${this._pdfDoc.numPages}`;\n }\n }\n /**\n * Creates a toolbar button with consistent styling\n */\n _createToolbarButton(icon, title, onClick) {\n const button = document.createElement(\"button\");\n button.textContent = icon;\n button.title = title;\n button.style.cssText = `\n background: transparent;\n border: none;\n color: #e8eaed;\n cursor: pointer;\n padding: 6px 8px;\n border-radius: 4px;\n font-size: 16px;\n line-height: 1;\n display: flex;\n align-items: center;\n justify-content: center;\n min-width: 32px;\n height: 32px;\n transition: background-color 0.2s;\n `;\n button.addEventListener(\"mouseenter\", () => {\n button.style.backgroundColor = \"rgba(255, 255, 255, 0.1)\";\n });\n button.addEventListener(\"mouseleave\", () => {\n button.style.backgroundColor = \"transparent\";\n });\n button.addEventListener(\"click\", (e) => {\n e.preventDefault();\n onClick();\n });\n return button;\n }\n /**\n * Zoom to a specific level (1.0 = 100%)\n */\n async _zoom(newZoom) {\n if (!this._pdfDoc || !this._mainContent) {\n console.warn(\"[PDF.js] Cannot zoom: PDF not loaded\");\n return;\n }\n this._currentZoom = Math.max(0.25, Math.min(4, newZoom));\n if (this._zoomDisplay) {\n this._zoomDisplay.textContent = `${Math.round(this._currentZoom * 100)}%`;\n }\n console.log(`[PDF.js] Zooming to ${Math.round(this._currentZoom * 100)}%...`);\n await this._rerenderPages();\n console.log(\"[PDF.js] \\u2705 Zoom complete\");\n }\n /**\n * Re-renders all pages (used by zoom and two-page view toggle)\n */\n async _rerenderPages() {\n if (!this._pdfDoc || !this._mainContent) return;\n const scrollPercentage = window.scrollY / (document.body.scrollHeight || 1);\n if (this._twoPageView) {\n this._mainContent.style.cssText = `\n flex: 1;\n overflow: visible;\n background-color: #525252;\n position: relative;\n padding: 0;\n box-sizing: border-box;\n display: grid;\n grid-template-columns: 1fr 1fr;\n align-items: start;\n `;\n } else {\n this._mainContent.style.cssText = `\n flex: 1;\n overflow: visible;\n background-color: #525252;\n position: relative;\n padding: 0;\n box-sizing: border-box;\n `;\n }\n this._mainContent.innerHTML = \"\";\n this._canvasElements = [];\n for (let pageNum = 1; pageNum <= this._pdfDoc.numPages; pageNum++) {\n await this._renderPage(pageNum);\n }\n setTimeout(() => {\n window.scrollTo(0, scrollPercentage * document.body.scrollHeight);\n }, 100);\n }\n /**\n * Fit page to available width\n */\n _fitToPage() {\n if (!this._mainContent) return;\n this._zoom(1);\n console.log(\"[PDF.js] Fit to page\");\n }\n /**\n * Rotate PDF pages clockwise by 90 degrees\n */\n _rotate() {\n var _a;\n this._rotation = (this._rotation + 90) % 360;\n const pages = (_a = this._mainContent) == null ? void 0 : _a.querySelectorAll(\"[data-page-number]\");\n if (pages) {\n pages.forEach((page) => {\n page.style.transform = `rotate(${this._rotation}deg)`;\n });\n }\n console.log(`[PDF.js] Rotated to ${this._rotation} degrees`);\n }\n /**\n * Download the PDF file\n */\n _download() {\n if (!this._pdfDataUrl) {\n console.error(\"[PDF.js] No PDF data URL available for download\");\n return;\n }\n const link = document.createElement(\"a\");\n link.href = this._pdfDataUrl;\n link.download = this._filename;\n link.style.display = \"none\";\n document.body.appendChild(link);\n link.click();\n document.body.removeChild(link);\n console.log(`[PDF.js] Downloaded: ${this._filename}`);\n }\n /**\n * Print the PDF by rendering all canvas pages into a new window\n */\n _print() {\n if (!this._canvasElements.length) {\n console.error(\"[PDF.js] No pages rendered to print\");\n return;\n }\n const printWindow = window.open(\"\", \"_blank\");\n if (!printWindow) {\n console.warn(\"[PDF.js] Print window blocked by browser\");\n return;\n }\n const doc = printWindow.document;\n doc.write(`<!DOCTYPE html><html><head>\n <title>${this._filename}</title>\n <style>\n * { margin: 0; padding: 0; box-sizing: border-box; }\n body { background: white; }\n img { display: block; width: 100%; page-break-after: always; page-break-inside: avoid; }\n img:last-child { page-break-after: avoid; }\n </style>\n </head><body>`);\n for (const canvas of this._canvasElements) {\n const dataUrl = canvas.toDataURL(\"image/png\");\n doc.write(`<img src=\"${dataUrl}\">`);\n }\n doc.write(\"</body></html>\");\n doc.close();\n printWindow.onload = () => {\n printWindow.print();\n printWindow.close();\n };\n setTimeout(() => {\n if (!printWindow.closed) {\n printWindow.print();\n printWindow.close();\n }\n }, 1500);\n console.log(\"[PDF.js] Print window opened\");\n }\n /**\n * Toggles the \"more options\" dropdown menu matching Chrome's PDF viewer\n */\n _toggleMoreMenu(anchorElement) {\n if (this._moreMenuElement) {\n this._closeMoreMenu();\n return;\n }\n const menu = document.createElement(\"div\");\n this._moreMenuElement = menu;\n menu.style.cssText = `\n position: fixed;\n background: #202124;\n border-radius: 4px;\n box-shadow: 0 2px 10px rgba(0,0,0,0.6);\n z-index: 2147483648;\n min-width: 220px;\n padding: 4px 0;\n font-family: 'Roboto', Arial, sans-serif;\n font-size: 14px;\n color: #e8eaed;\n `;\n const rect = anchorElement.getBoundingClientRect();\n menu.style.top = `${rect.bottom + 4}px`;\n menu.style.right = `${window.innerWidth - rect.right}px`;\n const addMenuItem = (text, checked, onClick) => {\n const item = document.createElement(\"div\");\n item.style.cssText = `\n padding: 10px 16px 10px 44px;\n cursor: pointer;\n position: relative;\n white-space: nowrap;\n `;\n if (checked !== null) {\n const checkEl = document.createElement(\"span\");\n checkEl.textContent = checked ? \"\\u2713\" : \"\";\n checkEl.style.cssText = `\n position: absolute;\n left: 16px;\n top: 50%;\n transform: translateY(-50%);\n font-size: 14px;\n `;\n item.appendChild(checkEl);\n }\n const label = document.createElement(\"span\");\n label.textContent = text;\n item.appendChild(label);\n item.addEventListener(\"mouseenter\", () => {\n item.style.backgroundColor = \"rgba(255,255,255,0.1)\";\n });\n item.addEventListener(\"mouseleave\", () => {\n item.style.backgroundColor = \"transparent\";\n });\n item.addEventListener(\"click\", () => {\n this._closeMoreMenu();\n onClick();\n });\n menu.appendChild(item);\n return item;\n };\n const addDivider = () => {\n const d = document.createElement(\"div\");\n d.style.cssText = \"height: 1px; background: rgba(255,255,255,0.15); margin: 4px 0;\";\n menu.appendChild(d);\n };\n addMenuItem(\"Two page view\", this._twoPageView, () => {\n this._twoPageView = !this._twoPageView;\n this._rerenderPages();\n });\n addMenuItem(\"Annotations\", this._annotationsVisible, () => {\n this._annotationsVisible = !this._annotationsVisible;\n console.log(`[PDF.js] Annotations ${this._annotationsVisible ? \"shown\" : \"hidden\"}`);\n });\n addDivider();\n addMenuItem(\"Present\", null, () => {\n this._present();\n });\n addMenuItem(\"Document properties\", null, () => {\n this._showDocumentProperties();\n });\n const onOutsideClick = (e) => {\n if (!menu.contains(e.target) && e.target !== anchorElement) {\n this._closeMoreMenu();\n }\n };\n setTimeout(() => {\n document.addEventListener(\"mousedown\", onOutsideClick, true);\n this._moreMenuCleanup = () => document.removeEventListener(\"mousedown\", onOutsideClick, true);\n }, 0);\n document.body.appendChild(menu);\n }\n /**\n * Closes the more options dropdown menu\n */\n _closeMoreMenu() {\n if (this._moreMenuElement) {\n this._moreMenuElement.remove();\n this._moreMenuElement = null;\n }\n if (this._moreMenuCleanup) {\n this._moreMenuCleanup();\n this._moreMenuCleanup = null;\n }\n }\n /**\n * Present mode — not yet implemented.\n */\n _present() {\n console.log(\"[PDF.js] Present: not yet implemented\");\n }\n /**\n * Shows a dialog with document metadata matching Chrome's \"Document properties\"\n */\n async _showDocumentProperties() {\n if (!this._pdfDoc) return;\n let info = {};\n try {\n const metadata = await this._pdfDoc.getMetadata();\n info = metadata.info || {};\n } catch (e) {\n }\n let fileSize = \"-\";\n if (this._pdfDataUrl) {\n try {\n const base64 = this._pdfDataUrl.split(\",\")[1];\n if (base64) {\n const bytes = Math.ceil(base64.length * 3 / 4);\n fileSize = bytes >= 1024 * 1024 ? `${(bytes / (1024 * 1024)).toFixed(1)} MB` : `${(bytes / 1024).toFixed(1)} KB`;\n }\n } catch (e) {\n }\n }\n const formatPdfDate = (raw) => {\n if (!raw) return \"-\";\n const m = raw.match(/^D:(\\d{4})(\\d{2})(\\d{2})(\\d{2})(\\d{2})(\\d{2})/);\n if (!m) return raw;\n const date = /* @__PURE__ */ new Date(`${m[1]}-${m[2]}-${m[3]}T${m[4]}:${m[5]}:${m[6]}`);\n return isNaN(date.getTime()) ? raw : date.toLocaleString();\n };\n let pageSize = \"-\";\n try {\n const firstPage = await this._pdfDoc.getPage(1);\n const vp = firstPage.getViewport({ scale: 1 });\n const wIn = (vp.width / 72).toFixed(2);\n const hIn = (vp.height / 72).toFixed(2);\n const orientation = vp.width > vp.height ? \"landscape\" : \"portrait\";\n pageSize = `${wIn} \\xD7 ${hIn} in (${orientation})`;\n } catch (e) {\n }\n const sections = [\n [\n [\"File name:\", this._filename],\n [\"File size:\", fileSize]\n ],\n [\n [\"Title:\", info[\"Title\"] || \"-\"],\n [\"Author:\", info[\"Author\"] || \"-\"],\n [\"Subject:\", info[\"Subject\"] || \"-\"],\n [\"Keywords:\", info[\"Keywords\"] || \"-\"],\n [\"Created:\", formatPdfDate(info[\"CreationDate\"] || \"\")],\n [\"Modified:\", formatPdfDate(info[\"ModDate\"] || \"\")],\n [\"Application:\", info[\"Creator\"] || \"-\"]\n ],\n [\n [\"PDF producer:\", info[\"Producer\"] || \"-\"],\n [\"PDF version:\", info[\"PDFFormatVersion\"] || \"-\"],\n [\"Page count:\", `${this._pdfDoc.numPages}`],\n [\"Page size:\", pageSize]\n ],\n [\n [\"Fast web view:\", \"No\"]\n ]\n ];\n const overlay = document.createElement(\"div\");\n overlay.style.cssText = `\n position: fixed;\n top: 0; left: 0; right: 0; bottom: 0;\n background: rgba(0,0,0,0.5);\n z-index: 2147483649;\n display: flex;\n align-items: center;\n justify-content: center;\n `;\n const dialog = document.createElement(\"div\");\n dialog.style.cssText = `\n background: #3c4043;\n border-radius: 12px;\n padding: 24px 24px 16px;\n min-width: 380px;\n max-width: 500px;\n color: #e8eaed;\n font-family: 'Roboto', Arial, sans-serif;\n box-shadow: 0 4px 20px rgba(0,0,0,0.5);\n `;\n const titleEl = document.createElement(\"h3\");\n titleEl.textContent = \"Document properties\";\n titleEl.style.cssText = \"margin: 0 0 16px; font-size: 18px; font-weight: 500;\";\n dialog.appendChild(titleEl);\n const addRow = (label, value) => {\n const row = document.createElement(\"div\");\n row.style.cssText = \"display: flex; padding: 7px 0; font-size: 13px;\";\n const labelEl = document.createElement(\"span\");\n labelEl.textContent = label;\n labelEl.style.cssText = \"min-width: 140px; flex-shrink: 0;\";\n const valueEl = document.createElement(\"span\");\n valueEl.textContent = value;\n valueEl.style.wordBreak = \"break-all\";\n row.appendChild(labelEl);\n row.appendChild(valueEl);\n dialog.appendChild(row);\n };\n const addDivider = () => {\n const d = document.createElement(\"div\");\n d.style.cssText = \"height: 1px; background: rgba(255,255,255,0.15); margin: 6px 0;\";\n dialog.appendChild(d);\n };\n for (let i = 0; i < sections.length; i++) {\n for (const [label, value] of sections[i]) {\n addRow(label, value);\n }\n if (i < sections.length - 1) {\n addDivider();\n }\n }\n const closeBtn = document.createElement(\"button\");\n closeBtn.textContent = \"Close\";\n closeBtn.style.cssText = `\n display: block;\n margin: 20px 0 0 auto;\n padding: 10px 28px;\n background: #8ab4f8;\n border: none;\n border-radius: 24px;\n color: #202124;\n font-size: 14px;\n font-weight: 500;\n cursor: pointer;\n font-family: 'Roboto', Arial, sans-serif;\n `;\n closeBtn.addEventListener(\"click\", () => overlay.remove());\n dialog.appendChild(closeBtn);\n overlay.appendChild(dialog);\n overlay.addEventListener(\"click\", (e) => {\n if (e.target === overlay) overlay.remove();\n });\n document.body.appendChild(overlay);\n console.log(\"[PDF.js] Document properties dialog opened\");\n }\n /**\n * Sets up scroll synchronization between the document scroll and the sidebar thumbnail highlight.\n * Pages are in the document flow so we listen on window, not on _mainContent.\n */\n _setupScrollSync() {\n if (!this._mainContent) return;\n window.addEventListener(\"scroll\", () => {\n if (!this._mainContent) return;\n const pages = this._mainContent.querySelectorAll(\"[data-page-number]\");\n const toolbarBottom = 56;\n for (let i = 0; i < pages.length; i++) {\n const pageElement = pages[i];\n const rect = pageElement.getBoundingClientRect();\n if (rect.top <= toolbarBottom + 100 && rect.bottom > toolbarBottom) {\n const pageNum = parseInt(pageElement.getAttribute(\"data-page-number\") || \"1\");\n this._updateCurrentPage(pageNum);\n break;\n }\n }\n }, { passive: true });\n }\n /**\n * Cleanup resources\n */\n cleanup() {\n if (this._pdfDoc) {\n this._pdfDoc.destroy();\n this._pdfDoc = null;\n }\n this._closeMoreMenu();\n this._canvasElements = [];\n this._thumbnailElements = [];\n this._toolbar = null;\n this._sidebar = null;\n this._mainContent = null;\n this._currentPage = 1;\n this._container.innerHTML = \"\";\n }\n};\n\n// packages/injected/src/recorder/skyramp/pdfViewerHelper.ts\nvar PdfViewerHelper = class {\n /**\n * Fetches a PDF using Playwright's backend (bypasses CORS) and returns it as a data URL\n */\n static async fetchPdfViaBackend(pdfUrl) {\n try {\n console.log(\"[PW-PDF-VIEWER] Fetching PDF via Playwright backend:\", pdfUrl.substring(0, 100));\n if (!window.__pw_recorderFetchPdf) {\n throw new Error(\"__pw_recorderFetchPdf binding not available\");\n }\n const dataUrl = await window.__pw_recorderFetchPdf(pdfUrl);\n if (!dataUrl) {\n throw new Error(\"Backend returned null (fetch failed)\");\n }\n console.log(\"[PW-PDF-VIEWER] \\u2705 Successfully fetched PDF via backend:\", dataUrl.substring(0, 100));\n return dataUrl;\n } catch (error) {\n console.error(\"[PW-PDF-VIEWER] \\u274C Failed to fetch PDF via backend:\", error);\n return null;\n }\n }\n /**\n * Fetches a PDF via backend and renders it with PDF.js\n */\n static async renderPdfWithPdfJs(options) {\n const { pdfUrl, containerElement, onSuccess, onError } = options;\n try {\n console.log(\"[PW-PDF-VIEWER] Starting PDF render process...\");\n const dataUrl = await this.fetchPdfViaBackend(pdfUrl);\n if (!dataUrl) {\n throw new Error(\"Failed to fetch PDF from backend\");\n }\n console.log(\"[PW-PDF-VIEWER] \\u2705 PDF fetched, initializing PDF.js viewer...\");\n let filename = \"Document.pdf\";\n try {\n const url = new URL(pdfUrl);\n const pathname = url.pathname;\n const lastSlash = pathname.lastIndexOf(\"/\");\n if (lastSlash !== -1) {\n filename = pathname.substring(lastSlash + 1);\n filename = decodeURIComponent(filename);\n }\n } catch (e) {\n console.warn(\"[PW-PDF-VIEWER] Failed to extract filename from URL:\", e);\n }\n const viewer = new PdfJsViewer(containerElement);\n await viewer.renderPdf({\n pdfDataUrl: dataUrl,\n filename,\n onReady: () => {\n console.log(\"[PW-PDF-VIEWER] \\u2705 PDF rendered successfully!\");\n if (onSuccess) {\n onSuccess();\n }\n },\n onError: (error) => {\n console.error(\"[PW-PDF-VIEWER] \\u274C PDF.js render error:\", error);\n if (onError) {\n onError(error);\n }\n }\n });\n return true;\n } catch (error) {\n console.error(\"[PW-PDF-VIEWER] \\u274C Failed to render PDF:\", error);\n if (onError) {\n onError(error);\n }\n return false;\n }\n }\n};\n\n// packages/injected/src/recorder/skyramp/pdfViewerTool.ts\nvar PdfViewerTool = class {\n constructor(recorder) {\n this._pdfEmbeds = /* @__PURE__ */ new Map();\n this._pdfPageReplaced = false;\n // Prevent infinite loop when replacing full-page PDF\n this._mutationObserver = null;\n this._recorder = recorder;\n }\n install() {\n console.log(\"[PDF-Tool] Installing PDF viewer tool...\");\n this._detectAndReplacePdfEmbeds();\n this._setupAutomaticPdfDetection();\n }\n uninstall() {\n console.log(\"[PDF-Tool] Uninstalling PDF viewer tool...\");\n if (this._mutationObserver) {\n this._mutationObserver.disconnect();\n this._mutationObserver = null;\n }\n }\n cleanup() {\n console.log(\"[PDF-Tool] Cleaning up PDF viewer tool...\");\n for (const [, data] of this._pdfEmbeds.entries()) {\n data.viewer.cleanup();\n }\n this._pdfEmbeds.clear();\n if (this._mutationObserver) {\n this._mutationObserver.disconnect();\n this._mutationObserver = null;\n }\n }\n /**\n * Checks if an element is within a PDF viewer context\n */\n isWithinPdfViewer(element) {\n if (!element)\n return false;\n let current = element;\n while (current) {\n if (this._pdfEmbeds.has(current)) {\n return true;\n }\n if (current.hasAttribute && current.hasAttribute(\"data-pw-pdf-viewer\")) {\n return true;\n }\n if (current.id === \"pw-pdf-viewer-container\") {\n return true;\n }\n current = current.parentNode;\n }\n return false;\n }\n /**\n * Sets up automatic PDF detection for dynamically added content\n */\n _setupAutomaticPdfDetection() {\n console.log(\"[PDF-Tool] Setting up automatic PDF detection...\");\n this._mutationObserver = new MutationObserver((mutations) => {\n for (const mutation of mutations) {\n if (mutation.type === \"childList\" && mutation.addedNodes.length > 0) {\n setTimeout(() => {\n this._detectAndReplacePdfEmbeds();\n }, 100);\n break;\n }\n }\n });\n this._mutationObserver.observe(this._recorder.document.body, {\n childList: true,\n subtree: true\n });\n console.log(\"[PDF-Tool] \\u2705 Automatic PDF detection active\");\n }\n /**\n * Detects PDF embeds in the page and replaces them with PDF.js viewers\n */\n async _detectAndReplacePdfEmbeds() {\n console.log(\"[PDF-Tool] Scanning for PDF embeds...\");\n if (this._pdfPageReplaced) {\n console.log(\"[PDF-Tool] PDF page already replaced, skipping detection\");\n return;\n }\n const isPdfPage = this._isCurrentPagePdf();\n if (isPdfPage) {\n console.log(\"[PDF-Tool] Current page is a PDF document, replacing with PDF.js viewer...\");\n await this._replacePdfPage();\n return;\n }\n const embeds = this._recorder.document.querySelectorAll('embed[type=\"application/pdf\"], iframe[src*=\".pdf\"]');\n if (embeds.length === 0) {\n console.log(\"[PDF-Tool] No PDF embeds found\");\n return;\n }\n console.log(`[PDF-Tool] Found ${embeds.length} PDF embed(s)`);\n for (const embed of embeds) {\n await this._replacePdfEmbed(embed);\n }\n }\n /**\n * Checks if the current page itself is a PDF document\n */\n _isCurrentPagePdf() {\n const url = window.location.href;\n const doc = this._recorder.document;\n if (url.toLowerCase().endsWith(\".pdf\")) {\n console.log(\"[PDF-Tool] URL ends with .pdf:\", url);\n return true;\n }\n const fullPageEmbed = doc.querySelector('embed[type=\"application/pdf\"]');\n if (fullPageEmbed && doc.body.children.length === 1) {\n console.log(\"[PDF-Tool] Found full-page PDF embed\");\n return true;\n }\n if (url.includes(\"s3.amazonaws.com\") || url.includes(\".s3.\")) {\n console.log(\"[PDF-Tool] S3 URL detected, likely a PDF:\", url);\n return true;\n }\n return false;\n }\n /**\n * Replaces the entire page with PDF.js viewer when the page itself is a PDF\n */\n async _replacePdfPage() {\n try {\n this._pdfPageReplaced = true;\n const pdfUrl = window.location.href;\n console.log(\"[PDF-Tool] Replacing full-page PDF with PDF.js viewer:\", pdfUrl.substring(0, 100));\n const doc = this._recorder.document;\n doc.body.innerHTML = \"\";\n const container = doc.createElement(\"div\");\n container.setAttribute(\"data-pw-pdf-viewer\", \"true\");\n container.id = \"pw-pdf-viewer-container\";\n container.style.cssText = `\n position: fixed;\n top: 0;\n left: 0;\n width: 100%;\n height: 100%;\n z-index: 2147483647;\n background: #525252;\n `;\n doc.body.appendChild(container);\n const viewer = new PdfJsViewer(container);\n const success = await PdfViewerHelper.renderPdfWithPdfJs({\n pdfUrl,\n containerElement: container,\n onSuccess: () => {\n console.log(\"[PDF-Tool] \\u2705 Full-page PDF replaced with PDF.js viewer\");\n },\n onError: (error) => {\n console.error(\"[PDF-Tool] \\u274C Failed to render full-page PDF:\", error);\n }\n });\n if (!success) {\n console.error(\"[PDF-Tool] Failed to render full-page PDF\");\n }\n } catch (error) {\n console.error(\"[PDF-Tool] Error replacing full-page PDF:\", error);\n }\n }\n /**\n * Replaces a single PDF embed with PDF.js viewer\n */\n async _replacePdfEmbed(embed) {\n try {\n if (this._pdfEmbeds.has(embed)) {\n console.log(\"[PDF-Tool] PDF embed already replaced, skipping...\");\n return;\n }\n let pdfUrl = embed.getAttribute(\"src\");\n if (!pdfUrl || pdfUrl === \"about:blank\") {\n console.log('[PDF-Tool] Embed has src=\"about:blank\", using page URL as PDF URL...');\n pdfUrl = window.location.href;\n if (!pdfUrl.includes(\".pdf\")) {\n console.log(\"[PDF-Tool] Page URL does not appear to be a PDF, skipping\");\n return;\n }\n console.log(\"[PDF-Tool] Using page URL as PDF:\", pdfUrl.substring(0, 100));\n }\n console.log(\"[PDF-Tool] Replacing PDF embed with PDF.js viewer:\", pdfUrl.substring(0, 100));\n const container = this._recorder.document.createElement(\"div\");\n container.setAttribute(\"data-pw-pdf-viewer\", \"true\");\n container.style.cssText = `\n position: absolute;\n top: ${embed.offsetTop}px;\n left: ${embed.offsetLeft}px;\n width: ${embed.offsetWidth || 800}px;\n height: ${embed.offsetHeight || 600}px;\n z-index: 2147483647;\n background: #525252;\n `;\n const parent = embed.parentNode;\n if (!parent) return;\n parent.insertBefore(container, embed);\n embed.style.display = \"none\";\n const viewer = new PdfJsViewer(container);\n this._pdfEmbeds.set(embed, { originalParent: parent, viewer, container });\n await PdfViewerHelper.renderPdfWithPdfJs({\n pdfUrl,\n containerElement: container,\n onSuccess: () => {\n console.log(\"[PDF-Tool] \\u2705 PDF embed replaced successfully\");\n },\n onError: (error) => {\n console.error(\"[PDF-Tool] \\u274C Failed to render PDF:\", error);\n embed.style.display = \"\";\n if (container.parentNode) {\n container.parentNode.removeChild(container);\n }\n this._pdfEmbeds.delete(embed);\n }\n });\n } catch (error) {\n console.error(\"[PDF-Tool] Error replacing PDF embed:\", error);\n }\n }\n};\n\n// packages/injected/src/recorder/skyramp/dragDropTool.ts\nfunction addEventListener(target, eventName, listener, options) {\n target.addEventListener(eventName, listener, options);\n const remove = () => {\n target.removeEventListener(eventName, listener, options);\n };\n return remove;\n}\nfunction removeEventListeners(listeners) {\n for (const listener of listeners)\n listener();\n listeners.splice(0, listeners.length);\n}\nfunction getTimestamp(recorder) {\n return recorder.injectedScript.utils.builtins.Date.now().toString();\n}\nvar _DragDropTool = class _DragDropTool {\n constructor(recorder) {\n this._dragState = null;\n this._listeners = [];\n this._lastClickTime = 0;\n this._lastClickTimeout = null;\n this._lastMousePosition = null;\n this._wheelToggleTimeout = null;\n this._isWheelSequence = false;\n this._wheelAccumulator = null;\n this._wheelDebounceTimeout = null;\n // PDF viewer tool (activated when PDF embeds are detected)\n this._pdfViewerTool = null;\n // Cleanup callbacks for always-on GoJS diagram listeners\n this._goJSAlwaysOnRemovers = [];\n this._recorder = recorder;\n }\n cursor() {\n return \"grab\";\n }\n install() {\n this._arm();\n this._checkAndActivatePdfViewer();\n this._hookGoJSDiagramsAlwaysOn();\n }\n uninstall() {\n if (this._lastClickTimeout) {\n clearTimeout(this._lastClickTimeout);\n this._lastClickTimeout = null;\n }\n if (this._wheelToggleTimeout) {\n clearTimeout(this._wheelToggleTimeout);\n this._wheelToggleTimeout = null;\n }\n this._flushWheelAction();\n if (this._wheelDebounceTimeout) {\n clearTimeout(this._wheelDebounceTimeout);\n this._wheelDebounceTimeout = null;\n }\n if (this._dragState && this._dragState.source && this._dragState.target) {\n this._capture();\n }\n for (const remove of this._goJSAlwaysOnRemovers)\n remove();\n this._goJSAlwaysOnRemovers = [];\n delete this._recorder.document.__skyrampGoJSHooked;\n this._disarm();\n }\n cleanup() {\n if (this._lastClickTimeout) {\n clearTimeout(this._lastClickTimeout);\n this._lastClickTimeout = null;\n }\n if (this._wheelToggleTimeout) {\n clearTimeout(this._wheelToggleTimeout);\n this._wheelToggleTimeout = null;\n }\n this._flushWheelAction();\n if (this._wheelDebounceTimeout) {\n clearTimeout(this._wheelDebounceTimeout);\n this._wheelDebounceTimeout = null;\n }\n if (this._dragState && this._dragState.source && this._dragState.target) {\n this._capture();\n }\n if (this._listeners.length > 0) {\n this._disarm();\n this._arm();\n }\n }\n onDblClick(event) {\n if (this._lastClickTimeout) {\n clearTimeout(this._lastClickTimeout);\n this._lastClickTimeout = null;\n }\n const target = event.target;\n if (!target || this._isPlaywrightElement(target))\n return;\n event.preventDefault();\n try {\n const action = {\n name: \"click\",\n selector: \"body\",\n button: \"left\",\n modifiers: 0,\n clickCount: 2,\n // Double-click\n position: {\n x: Math.round(event.clientX),\n y: Math.round(event.clientY)\n },\n signals: [],\n timestamp: getTimestamp(this._recorder)\n };\n this._recorder.recordAction(action);\n this._deactivate();\n } catch (error) {\n console.error(\"[PW-RECORDER] Error recording position-based double-click:\", error);\n this._deactivate();\n }\n }\n _flushWheelAction() {\n if (!this._wheelAccumulator)\n return;\n const absX = Math.abs(this._wheelAccumulator.deltaX);\n const absY = Math.abs(this._wheelAccumulator.deltaY);\n if (absX < _DragDropTool.WHEEL_NOISE_AXIS_THRESHOLD && absY < _DragDropTool.WHEEL_NOISE_AXIS_THRESHOLD) {\n console.log(\"[PW-RECORDER] Suppressing Magic Mouse noise wheel:\", {\n deltaX: this._wheelAccumulator.deltaX,\n deltaY: this._wheelAccumulator.deltaY,\n accumulatedFor: Date.now() - this._wheelAccumulator.startTime + \"ms\"\n });\n this._wheelAccumulator = null;\n return;\n }\n try {\n const action = {\n name: \"mouse.wheel\",\n position: this._wheelAccumulator.position,\n deltaX: this._wheelAccumulator.deltaX,\n deltaY: this._wheelAccumulator.deltaY,\n deltaZ: this._wheelAccumulator.deltaZ,\n modifiers: this._wheelAccumulator.modifiers,\n signals: [],\n timestamp: getTimestamp(this._recorder)\n };\n this._recorder.recordAction(action);\n console.log(\"[PW-RECORDER] Flushed accumulated wheel action:\", {\n deltaX: action.deltaX,\n deltaY: action.deltaY,\n deltaZ: action.deltaZ,\n accumulatedFor: Date.now() - this._wheelAccumulator.startTime + \"ms\"\n });\n } catch (error) {\n console.error(\"[PW-RECORDER] Error flushing wheel action:\", error);\n }\n this._wheelAccumulator = null;\n }\n _arm() {\n var _a;\n this._dragState = {\n source: null,\n target: null,\n sourcePoint: null,\n targetPoint: null,\n startTime: Date.now(),\n isCanvas: false,\n isGoJS: false,\n isReactFlow: false,\n isSlider: false,\n dropDetected: false,\n captured: false\n };\n (_a = this._recorder.injectedScript.document.body) == null ? void 0 : _a.setAttribute(\"data-pw-cursor\", \"grab\");\n const onDragStart = (e) => {\n const dragEvent = e;\n if (this._dragState) {\n const sourceElement = this._recorder.document.elementFromPoint(dragEvent.clientX, dragEvent.clientY);\n if (sourceElement && !this._isPlaywrightElement(sourceElement)) {\n this._dragState.source = this._selectDraggableAncestor(sourceElement);\n this._dragState.sourcePoint = { x: dragEvent.clientX, y: dragEvent.clientY };\n } else {\n }\n }\n };\n const onDragOver = (e) => {\n const dragEvent = e;\n if (this._dragState && this._dragState.source) {\n const targetElement = this._recorder.document.elementFromPoint(dragEvent.clientX, dragEvent.clientY);\n if (targetElement && !this._isPlaywrightElement(targetElement)) {\n const potentialTarget = this._selectDroppableAncestor(targetElement);\n if (potentialTarget !== this._dragState.source) {\n this._dragState.target = potentialTarget;\n this._dragState.targetPoint = { x: dragEvent.clientX, y: dragEvent.clientY };\n }\n }\n }\n };\n const onPointerDown = (e) => {\n const pointerEvent = e;\n if (this._dragState) {\n this._dragState.source = null;\n this._dragState.target = null;\n this._dragState.sourcePoint = null;\n this._dragState.targetPoint = null;\n this._dragState.isCanvas = false;\n this._dragState.isGoJS = false;\n this._dragState.isReactFlow = false;\n this._dragState.isSlider = false;\n this._dragState.dropDetected = false;\n this._dragState.startTime = Date.now();\n }\n if (this._dragState) {\n const sourceElement = this._recorder.document.elementFromPoint(pointerEvent.clientX, pointerEvent.clientY);\n if (sourceElement && !this._isPlaywrightElement(sourceElement)) {\n if (this._isReactFlowElement(sourceElement)) {\n this._dragState.isReactFlow = true;\n let reactFlowContainer = sourceElement;\n while (reactFlowContainer && !reactFlowContainer.classList.contains(\"react-flow\")) {\n reactFlowContainer = reactFlowContainer.parentElement;\n }\n this._dragState.source = reactFlowContainer || sourceElement;\n this._dragState.sourcePoint = { x: pointerEvent.clientX, y: pointerEvent.clientY };\n } else if (this._isGoJSElement(sourceElement)) {\n this._dragState.isGoJS = true;\n this._dragState.source = sourceElement;\n this._dragState.sourcePoint = { x: pointerEvent.clientX, y: pointerEvent.clientY };\n } else if (this._isCanvasElement(sourceElement)) {\n this._dragState.isCanvas = true;\n this._dragState.source = sourceElement;\n this._dragState.sourcePoint = { x: pointerEvent.clientX, y: pointerEvent.clientY };\n } else {\n const draggableElement = this._selectDraggableAncestor(sourceElement);\n if (draggableElement !== sourceElement) {\n this._dragState.source = draggableElement;\n this._dragState.sourcePoint = { x: pointerEvent.clientX, y: pointerEvent.clientY };\n } else {\n const sliderThumb = this._findSliderThumb(sourceElement);\n if (sliderThumb) {\n this._dragState.isSlider = true;\n this._dragState.source = sliderThumb;\n this._dragState.sourcePoint = { x: pointerEvent.clientX, y: pointerEvent.clientY };\n } else {\n this._dragState.source = sourceElement;\n this._dragState.sourcePoint = { x: pointerEvent.clientX, y: pointerEvent.clientY };\n }\n }\n }\n }\n }\n };\n const onPointerMove = (e) => {\n const pointerEvent = e;\n if (this._dragState && this._dragState.source && pointerEvent.buttons === 1) {\n const targetElement = this._recorder.document.elementFromPoint(pointerEvent.clientX, pointerEvent.clientY);\n if (targetElement && !this._isPlaywrightElement(targetElement)) {\n if (this._dragState.isReactFlow && this._isReactFlowElement(targetElement)) {\n this._dragState.target = this._dragState.source;\n this._dragState.targetPoint = { x: pointerEvent.clientX, y: pointerEvent.clientY };\n } else if (this._dragState.isGoJS && this._isCanvasElement(targetElement)) {\n this._dragState.target = targetElement;\n this._dragState.targetPoint = { x: pointerEvent.clientX, y: pointerEvent.clientY };\n } else if (this._dragState.isCanvas && this._isCanvasElement(targetElement)) {\n this._dragState.target = targetElement;\n this._dragState.targetPoint = { x: pointerEvent.clientX, y: pointerEvent.clientY };\n } else if (this._dragState.isSlider) {\n this._dragState.target = this._dragState.source;\n this._dragState.targetPoint = { x: pointerEvent.clientX, y: pointerEvent.clientY };\n } else if (!this._dragState.isCanvas && !this._dragState.isReactFlow && !this._dragState.isSlider) {\n const potentialTarget = this._selectDroppableAncestor(targetElement);\n if (potentialTarget !== this._dragState.source) {\n this._dragState.target = potentialTarget;\n this._dragState.targetPoint = { x: pointerEvent.clientX, y: pointerEvent.clientY };\n } else if (targetElement !== this._dragState.source) {\n this._dragState.target = targetElement;\n this._dragState.targetPoint = { x: pointerEvent.clientX, y: pointerEvent.clientY };\n }\n }\n }\n }\n };\n const onPointerUp = (e) => {\n const pointerEvent = e;\n if (this._dragState && this._dragState.source && this._dragState.sourcePoint) {\n if (this._dragState.target) {\n const sourceColumn = this._getSourceColumn(this._dragState.source);\n const targetColumn = this._dragState.target;\n this._capture();\n } else {\n const distance = Math.sqrt(\n Math.pow(pointerEvent.clientX - this._dragState.sourcePoint.x, 2) + Math.pow(pointerEvent.clientY - this._dragState.sourcePoint.y, 2)\n );\n if (distance >= 5) {\n this._deactivate();\n return;\n }\n if (distance < 5) {\n if (pointerEvent.button === 2) {\n return;\n }\n if (this._lastClickTimeout) {\n clearTimeout(this._lastClickTimeout);\n }\n this._lastClickTimeout = setTimeout(() => {\n try {\n const action = {\n name: \"click\",\n selector: \"body\",\n button: \"left\",\n modifiers: 0,\n clickCount: 1,\n position: {\n x: Math.round(pointerEvent.clientX),\n y: Math.round(pointerEvent.clientY)\n },\n signals: [],\n timestamp: getTimestamp(this._recorder)\n };\n this._recorder.recordAction(action);\n this._deactivate();\n } catch (error) {\n console.error(\"[PW-RECORDER] Error recording position-based click:\", error);\n this._deactivate();\n }\n this._lastClickTimeout = null;\n }, 300);\n }\n }\n } else {\n console.log(\"[PW-RECORDER] PointerUp - no valid drag state\");\n }\n };\n const onDrop = (e) => {\n const dragEvent = e;\n if (this._dragState && this._dragState.source) {\n this._dragState.dropDetected = true;\n if (!this._dragState.target) {\n const targetElement = this._recorder.document.elementFromPoint(dragEvent.clientX, dragEvent.clientY);\n if (targetElement && !this._isPlaywrightElement(targetElement)) {\n this._dragState.target = this._selectDroppableAncestor(targetElement);\n this._dragState.targetPoint = { x: dragEvent.clientX, y: dragEvent.clientY };\n }\n }\n if (this._dragState.target) {\n const sourceColumn = this._getSourceColumn(this._dragState.source);\n const targetColumn = this._dragState.target;\n this._capture();\n } else {\n this._deactivate();\n }\n } else {\n this._deactivate();\n }\n };\n const onDragEnd = (e) => {\n var _a2;\n const dragEvent = e;\n if (this._dragState && this._dragState.source) {\n if (this._dragState.dropDetected) {\n if (!this._dragState.target) {\n const targetElement = this._recorder.document.elementFromPoint(dragEvent.clientX, dragEvent.clientY);\n if (targetElement && !this._isPlaywrightElement(targetElement)) {\n this._dragState.target = this._selectDroppableAncestor(targetElement);\n this._dragState.targetPoint = { x: dragEvent.clientX, y: dragEvent.clientY };\n }\n }\n if (this._dragState.target) {\n const sourceColumn = this._getSourceColumn(this._dragState.source);\n const targetColumn = this._dragState.target;\n this._capture();\n }\n } else {\n const dropEffect = (_a2 = dragEvent.dataTransfer) == null ? void 0 : _a2.dropEffect;\n if (dropEffect && dropEffect !== \"none\") {\n this._deactivate();\n return;\n }\n console.log(\"[PW-RECORDER] Drag cancelled (no drop event), not recording\");\n }\n this._dragState = {\n source: null,\n target: null,\n sourcePoint: null,\n targetPoint: null,\n startTime: Date.now(),\n isCanvas: false,\n isGoJS: false,\n isReactFlow: false,\n isSlider: false,\n dropDetected: false,\n captured: false\n };\n }\n };\n const onWheel = (e) => {\n var _a2;\n const wheelEvent = e;\n const target = wheelEvent.target;\n if (!target || this._isPlaywrightElement(target))\n return;\n if (this._isGoJSElement(target))\n return;\n const isInPdfViewer = ((_a2 = this._pdfViewerTool) == null ? void 0 : _a2.isWithinPdfViewer(target)) || false;\n if (wheelEvent.ctrlKey && !isInPdfViewer) {\n e.preventDefault();\n }\n let modifiers = 0;\n if (wheelEvent.altKey)\n modifiers |= 1;\n if (wheelEvent.ctrlKey)\n modifiers |= 2;\n if (wheelEvent.metaKey)\n modifiers |= 4;\n if (wheelEvent.shiftKey)\n modifiers |= 8;\n try {\n const currentPosition = {\n x: Math.round(wheelEvent.clientX),\n y: Math.round(wheelEvent.clientY)\n };\n if (!this._isWheelSequence) {\n this._isWheelSequence = true;\n const commentAction = {\n name: \"comment\",\n text: \"Mouse scrolling block\",\n signals: [],\n timestamp: getTimestamp(this._recorder)\n };\n this._recorder.recordAction(commentAction);\n const timeoutAction = {\n name: \"waitForTimeout\",\n duration: _DragDropTool.WHEEL_SCROLL_TIMEOUT_MS,\n signals: [],\n timestamp: getTimestamp(this._recorder)\n };\n this._recorder.recordAction(timeoutAction);\n }\n if (!this._lastMousePosition || this._lastMousePosition.x !== currentPosition.x || this._lastMousePosition.y !== currentPosition.y) {\n const mouseMoveAction = {\n name: \"mouse.move\",\n position: currentPosition,\n signals: [],\n timestamp: getTimestamp(this._recorder)\n };\n this._recorder.recordAction(mouseMoveAction);\n this._lastMousePosition = currentPosition;\n }\n const now = Date.now();\n if (!this._wheelAccumulator) {\n this._wheelAccumulator = {\n deltaX: wheelEvent.deltaX,\n deltaY: wheelEvent.deltaY,\n deltaZ: wheelEvent.deltaZ,\n position: currentPosition,\n modifiers,\n startTime: now\n };\n } else {\n this._wheelAccumulator.deltaX += wheelEvent.deltaX;\n this._wheelAccumulator.deltaY += wheelEvent.deltaY;\n this._wheelAccumulator.deltaZ += wheelEvent.deltaZ;\n this._wheelAccumulator.position = currentPosition;\n this._wheelAccumulator.modifiers = modifiers;\n if (now - this._wheelAccumulator.startTime > _DragDropTool.WHEEL_MAX_ACCUMULATION_MS) {\n this._flushWheelAction();\n this._wheelAccumulator = {\n deltaX: wheelEvent.deltaX,\n deltaY: wheelEvent.deltaY,\n deltaZ: wheelEvent.deltaZ,\n position: currentPosition,\n modifiers,\n startTime: now\n };\n }\n }\n if (this._wheelDebounceTimeout) {\n clearTimeout(this._wheelDebounceTimeout);\n }\n this._wheelDebounceTimeout = setTimeout(() => {\n this._flushWheelAction();\n this._wheelDebounceTimeout = null;\n }, _DragDropTool.WHEEL_DEBOUNCE_MS);\n if (this._wheelToggleTimeout) {\n clearTimeout(this._wheelToggleTimeout);\n }\n this._wheelToggleTimeout = setTimeout(() => {\n this._deactivate();\n }, _DragDropTool.WHEEL_TOOL_DISABLE_MS);\n } catch (error) {\n console.error(\"[PW-RECORDER] Error recording wheel event:\", error);\n }\n };\n const onContextMenu = (e) => {\n const contextEvent = e;\n const target = contextEvent.target;\n if (!target || this._isPlaywrightElement(target))\n return;\n e.preventDefault();\n try {\n this._lastMousePosition = { x: Math.round(contextEvent.clientX), y: Math.round(contextEvent.clientY) };\n const action = {\n name: \"click\",\n selector: \"body\",\n button: \"right\",\n modifiers: 0,\n clickCount: 1,\n position: {\n x: Math.round(contextEvent.clientX),\n y: Math.round(contextEvent.clientY)\n },\n signals: [],\n timestamp: getTimestamp(this._recorder)\n };\n this._recorder.recordAction(action);\n this._deactivate();\n } catch (error) {\n console.error(\"[PW-RECORDER] Error recording right-click:\", error);\n this._deactivate();\n }\n };\n this._listeners = [\n addEventListener(this._recorder.document, \"dragstart\", onDragStart, true),\n addEventListener(this._recorder.document, \"dragover\", onDragOver, true),\n addEventListener(this._recorder.document, \"drop\", onDrop, true),\n addEventListener(this._recorder.document, \"dragend\", onDragEnd, true),\n addEventListener(this._recorder.document, \"pointerdown\", onPointerDown, true),\n addEventListener(this._recorder.document, \"pointermove\", onPointerMove, true),\n addEventListener(this._recorder.document, \"pointerup\", onPointerUp, true),\n addEventListener(this._recorder.document, \"wheel\", onWheel, { passive: false, capture: true }),\n addEventListener(this._recorder.document, \"contextmenu\", onContextMenu, true)\n ];\n }\n _disarm() {\n removeEventListeners(this._listeners);\n this._listeners = [];\n this._dragState = null;\n }\n // Deselect the DD tool after a completed (or aborted) drag interaction.\n // setMode() alone is not enough: in deeply-nested iframes the broadcast\n // round-trip is slow enough that the next user input gets captured by\n // this tool's still-armed listeners. Disarm locally first, then signal\n // the mode change for the rest of the recorder to follow.\n _deactivate() {\n if (this._lastClickTimeout) {\n clearTimeout(this._lastClickTimeout);\n this._lastClickTimeout = null;\n }\n if (this._wheelToggleTimeout) {\n clearTimeout(this._wheelToggleTimeout);\n this._wheelToggleTimeout = null;\n }\n if (this._wheelDebounceTimeout) {\n clearTimeout(this._wheelDebounceTimeout);\n this._wheelDebounceTimeout = null;\n }\n this._isWheelSequence = false;\n this._wheelAccumulator = null;\n this._disarm();\n if (this._recorder.state.mode === \"recordingDrag\")\n this._recorder.setMode(\"recording\");\n }\n /**\n * Checks if there are PDF embeds on the page and activates PDF viewer tool if needed\n */\n _checkAndActivatePdfViewer() {\n const doc = this._recorder.document;\n if (window.__pwPdfViewerInstalled) {\n console.log(\"[DD-Tool] PDF viewer already installed, skipping\");\n return;\n }\n const hasPdfEmbed = doc.querySelector('embed[type=\"application/pdf\"], iframe[src*=\".pdf\"]');\n const isPdfPage = window.location.href.toLowerCase().endsWith(\".pdf\") || window.location.href.includes(\"s3.amazonaws.com\") || window.location.href.includes(\".s3.\");\n if (hasPdfEmbed || isPdfPage) {\n console.log(\"[DD-Tool] PDF detected, activating PDF viewer tool...\");\n this._pdfViewerTool = new PdfViewerTool(this._recorder);\n this._pdfViewerTool.install();\n window.__pwPdfViewerInstalled = true;\n console.log(\"[DD-Tool] \\u2705 PDF viewer tool activated (permanent)\");\n }\n }\n _getActualPageElement(composedPath) {\n for (const target of composedPath) {\n const element = target;\n if (element && element.nodeType === Node.ELEMENT_NODE && !this._isPlaywrightElement(element)) {\n return element;\n }\n }\n return null;\n }\n _isPlaywrightElement(element) {\n var _a;\n const nodeName = ((_a = element.nodeName) == null ? void 0 : _a.toLowerCase()) || \"\";\n const id = element.id || \"\";\n const isPlaywright = nodeName.startsWith(\"x-pw-\") || id === \"x-pw-glass\" || element.classList.contains(\"playwright-overlay\") || element.hasAttribute(\"data-playwright\");\n return isPlaywright;\n }\n _isCanvasElement(element) {\n var _a;\n return ((_a = element.tagName) == null ? void 0 : _a.toLowerCase()) === \"canvas\";\n }\n _isGoJSElement(element) {\n var _a, _b;\n if (((_a = element.tagName) == null ? void 0 : _a.toLowerCase()) !== \"canvas\")\n return false;\n const win = (_b = element.ownerDocument) == null ? void 0 : _b.defaultView;\n return !!((win == null ? void 0 : win.myDiagram) || (win == null ? void 0 : win.myPalette));\n }\n /**\n * Walks up from a canvas element to find the GoJS Diagram/Palette that owns it,\n * using the generic go.Diagram.fromDiv() API (works for any GoJS application).\n * Returns the diagram instance, whether it is a Palette, and a CSS selector\n * for the container div derived from the element's actual DOM attributes.\n */\n _findGoJSContainer(canvas) {\n var _a, _b, _c;\n const win = (_a = canvas.ownerDocument) == null ? void 0 : _a.defaultView;\n if (!((_c = (_b = win == null ? void 0 : win.go) == null ? void 0 : _b.Diagram) == null ? void 0 : _c.fromDiv))\n return null;\n let el = canvas.parentElement;\n while (el && el !== canvas.ownerDocument.body) {\n const diagram = win.go.Diagram.fromDiv(el);\n if (diagram) {\n const isPalette = !!(win.go.Palette && diagram instanceof win.go.Palette);\n let containerSelector;\n if (el.id) {\n containerSelector = `#${el.id}`;\n } else if (el.getAttribute(\"data-testid\")) {\n containerSelector = `[data-testid=\"${el.getAttribute(\"data-testid\")}\"]`;\n } else {\n const parent = el.parentElement;\n if (parent) {\n const idx = Array.from(parent.children).indexOf(el) + 1;\n containerSelector = `${el.tagName.toLowerCase()}:nth-child(${idx})`;\n } else {\n containerSelector = el.tagName.toLowerCase();\n }\n }\n return { diagram, isPalette, containerSelector };\n }\n el = el.parentElement;\n }\n return null;\n }\n /**\n * Build a stable CSS selector from a DOM element — same logic as gojsLinkTool._buildSelector.\n */\n _buildSelectorFromEl(el) {\n if (el.id)\n return `#${el.id}`;\n if (el.getAttribute(\"data-testid\"))\n return `[data-testid=\"${el.getAttribute(\"data-testid\")}\"]`;\n const parent = el.parentElement;\n if (parent) {\n const idx = Array.from(parent.children).indexOf(el) + 1;\n return `${el.tagName.toLowerCase()}:nth-child(${idx})`;\n }\n return el.tagName.toLowerCase();\n }\n /**\n * Emit a diagramNodeAdd action for a GoJS palette drop, with full anchor computation.\n * Mirrors gojsLinkTool._emitDiagramNodeAdd so normal recording mode produces the\n * same JSONL as gojsLinkTool mode.\n */\n _emitGoJSNodeAdd(diagram, sourcePanelSelector, targetPanelSelector, category, key, docX, docY) {\n let anchorKey;\n let anchorOffsetX;\n let anchorOffsetY;\n let anchorDocX;\n let anchorDocY;\n let minDist = Infinity;\n diagram.nodes.each((node) => {\n var _a;\n if (!(node == null ? void 0 : node.data)) return;\n const nKey = String((_a = node.data.key) != null ? _a : \"\");\n if (!nKey || nKey === key) return;\n const dx = node.location.x - docX;\n const dy = node.location.y - docY;\n const dist = Math.sqrt(dx * dx + dy * dy);\n if (dist < minDist) {\n minDist = dist;\n anchorKey = nKey;\n anchorOffsetX = Math.round(docX - node.location.x);\n anchorOffsetY = Math.round(docY - node.location.y);\n anchorDocX = Math.round(node.location.x);\n anchorDocY = Math.round(node.location.y);\n }\n });\n const action = {\n name: \"diagramNodeAdd\",\n diagramType: \"gojs\",\n sourcePanelSelector,\n targetPanelSelector,\n sourceIsPalette: true,\n targetIsPalette: false,\n sourceCategory: category,\n sourceKey: key,\n targetDocX: Math.round(docX),\n targetDocY: Math.round(docY),\n anchorKey,\n anchorOffsetX,\n anchorOffsetY,\n anchorDocX,\n anchorDocY,\n signals: [],\n timestamp: getTimestamp(this._recorder)\n };\n this._recorder.recordAction(action);\n this._deactivate();\n }\n /**\n * Emit a diagramLinkAdd action for a GoJS link drawn in normal recording mode.\n * Mirrors gojsLinkTool._emitDiagramLinkAdd.\n */\n _emitGoJSLinkAdd(fromKey, toKey, fromPort, toPort, panelSelector) {\n const action = {\n name: \"diagramLinkAdd\",\n diagramType: \"gojs\",\n panelSelector,\n fromKey,\n toKey,\n fromPort,\n toPort,\n signals: [],\n timestamp: getTimestamp(this._recorder)\n };\n this._recorder.recordAction(action);\n this._deactivate();\n }\n /**\n * Register always-on GoJS diagram listeners for ExternalObjectsDropped and LinkDrawn.\n * Called from install() so GoJS events are captured even in normal recording mode.\n *\n * Guards:\n * - __skyrampGoJSHooked: prevents double-registration on the same document.\n * - __skyrampGojsLinkToolActive: always-on handlers skip emission when gojsLinkTool\n * is active (it handles the same events with higher fidelity).\n */\n _hookGoJSDiagramsAlwaysOn() {\n var _a, _b;\n const doc = this._recorder.document;\n if (doc.__skyrampGoJSHooked)\n return;\n const win = doc.defaultView;\n if (!((_b = (_a = win == null ? void 0 : win.go) == null ? void 0 : _a.Diagram) == null ? void 0 : _b.fromDiv))\n return;\n const canvases = Array.from(doc.querySelectorAll(\"canvas\"));\n if (!canvases.length)\n return;\n const hookedDiagrams = /* @__PURE__ */ new Set();\n for (const canvas of canvases) {\n let el = canvas.parentElement;\n while (el && el !== doc.body) {\n const diagram = win.go.Diagram.fromDiv(el);\n if (diagram) {\n const isPalette = !!(win.go.Palette && diagram instanceof win.go.Palette);\n if (!isPalette && !hookedDiagrams.has(diagram)) {\n hookedDiagrams.add(diagram);\n const panelSelector = this._buildSelectorFromEl(el);\n const externalDropHandler = (e) => {\n if (doc.__skyrampGojsLinkToolActive) return;\n e.subject.each((part) => {\n var _a2, _b2;\n if (!(part == null ? void 0 : part.data)) return;\n if (part.data.from !== void 0) return;\n const category = String((_a2 = part.data.category) != null ? _a2 : \"\");\n const key = String((_b2 = part.data.key) != null ? _b2 : \"\");\n const loc = part.location;\n let sourcePanelSelector = \"\";\n try {\n const palCanvases = Array.from(doc.querySelectorAll(\"canvas\"));\n for (const pc of palCanvases) {\n let pel = pc.parentElement;\n while (pel && pel !== doc.body) {\n const pd = win.go.Diagram.fromDiv(pel);\n if (pd && win.go.Palette && pd instanceof win.go.Palette) {\n sourcePanelSelector = this._buildSelectorFromEl(pel);\n break;\n }\n pel = pel.parentElement;\n }\n if (sourcePanelSelector) break;\n }\n } catch (_) {\n }\n setTimeout(() => {\n var _a3, _b3, _c;\n const finalLoc = (_a3 = part.location) != null ? _a3 : loc;\n this._emitGoJSNodeAdd(diagram, sourcePanelSelector, panelSelector, category, key, (_b3 = finalLoc == null ? void 0 : finalLoc.x) != null ? _b3 : 0, (_c = finalLoc == null ? void 0 : finalLoc.y) != null ? _c : 0);\n }, 0);\n });\n };\n const linkDrawnHandler = (e) => {\n var _a2, _b2, _c, _d;\n if (doc.__skyrampGojsLinkToolActive) return;\n const link = e.subject;\n if (!(link == null ? void 0 : link.data)) return;\n const fromKey = String((_a2 = link.data.from) != null ? _a2 : \"\");\n const toKey = String((_b2 = link.data.to) != null ? _b2 : \"\");\n if (!fromKey || !toKey) return;\n this._emitGoJSLinkAdd(\n fromKey,\n toKey,\n String((_c = link.data.fromPort) != null ? _c : \"\"),\n String((_d = link.data.toPort) != null ? _d : \"\"),\n panelSelector\n );\n };\n diagram.addDiagramListener(\"ExternalObjectsDropped\", externalDropHandler);\n diagram.addDiagramListener(\"LinkDrawn\", linkDrawnHandler);\n this._goJSAlwaysOnRemovers.push(() => {\n try {\n diagram.removeDiagramListener(\"ExternalObjectsDropped\", externalDropHandler);\n diagram.removeDiagramListener(\"LinkDrawn\", linkDrawnHandler);\n } catch (_) {\n }\n });\n }\n break;\n }\n el = el.parentElement;\n }\n }\n if (hookedDiagrams.size > 0) {\n doc.__skyrampGoJSHooked = true;\n console.log(\"[DragDropTool] always-on GoJS listeners registered on\", hookedDiagrams.size, \"diagram(s)\");\n }\n }\n _captureGoJSDrag() {\n var _a, _b, _c, _d, _e, _f, _g, _h, _i, _j, _k;\n if (!((_a = this._dragState) == null ? void 0 : _a.source) || !((_b = this._dragState) == null ? void 0 : _b.target) || !((_c = this._dragState) == null ? void 0 : _c.sourcePoint) || !((_d = this._dragState) == null ? void 0 : _d.targetPoint)) {\n return;\n }\n try {\n const srcCanvas = this._dragState.source;\n const dstCanvas = this._dragState.target;\n const win = (_e = srcCanvas.ownerDocument) == null ? void 0 : _e.defaultView;\n const srcInfo = this._findGoJSContainer(srcCanvas);\n const dstInfo = this._findGoJSContainer(dstCanvas);\n const sourcePanelSelector = (_f = srcInfo == null ? void 0 : srcInfo.containerSelector) != null ? _f : \"\";\n const targetPanelSelector = (_g = dstInfo == null ? void 0 : dstInfo.containerSelector) != null ? _g : \"\";\n const sourceIsPalette = (_h = srcInfo == null ? void 0 : srcInfo.isPalette) != null ? _h : false;\n const targetIsPalette = (_i = dstInfo == null ? void 0 : dstInfo.isPalette) != null ? _i : false;\n let sourceCategory = \"\";\n let sourceKey = \"\";\n const srcDiagram = srcInfo == null ? void 0 : srcInfo.diagram;\n if (srcDiagram && (win == null ? void 0 : win.go)) {\n const srcRect = srcCanvas.getBoundingClientRect();\n const vpX = this._dragState.sourcePoint.x - srcRect.left;\n const vpY = this._dragState.sourcePoint.y - srcRect.top;\n try {\n const docPt = srcDiagram.transformViewToDoc(new win.go.Point(vpX, vpY));\n const part = srcDiagram.findPartAt(docPt, false);\n if (part == null ? void 0 : part.data) {\n sourceCategory = (_j = part.data.category) != null ? _j : \"\";\n sourceKey = String((_k = part.data.key) != null ? _k : \"\");\n }\n } catch (_e2) {\n }\n }\n let targetDocX = 0;\n let targetDocY = 0;\n const dstDiagram = dstInfo == null ? void 0 : dstInfo.diagram;\n if (dstDiagram && (win == null ? void 0 : win.go)) {\n const dstRect = dstCanvas.getBoundingClientRect();\n const vpX = this._dragState.targetPoint.x - dstRect.left;\n const vpY = this._dragState.targetPoint.y - dstRect.top;\n try {\n const docPt = dstDiagram.transformViewToDoc(new win.go.Point(vpX, vpY));\n targetDocX = Math.round(docPt.x);\n targetDocY = Math.round(docPt.y);\n } catch (_e2) {\n }\n }\n if (!sourceIsPalette && sourceKey === \"\") {\n this._deactivate();\n return;\n }\n const doc = srcCanvas.ownerDocument;\n if (sourceIsPalette && doc.__skyrampGoJSHooked) {\n this._deactivate();\n return;\n }\n const action = {\n name: \"diagramNodeAdd\",\n diagramType: \"gojs\",\n sourcePanelSelector,\n targetPanelSelector,\n sourceIsPalette,\n targetIsPalette,\n sourceCategory,\n sourceKey,\n targetDocX,\n targetDocY,\n signals: [],\n timestamp: getTimestamp(this._recorder)\n };\n this._recorder.recordAction(action);\n this._deactivate();\n } catch (error) {\n console.error(\"[PW-RECORDER] Error capturing GoJS drag:\", error);\n this._captureCanvasDrag();\n return;\n }\n this._dragState = {\n source: null,\n target: null,\n sourcePoint: null,\n targetPoint: null,\n startTime: Date.now(),\n isCanvas: false,\n isGoJS: false,\n isReactFlow: false,\n isSlider: false,\n dropDetected: false,\n captured: false\n };\n }\n _isReactFlowElement(element) {\n let current = element;\n while (current) {\n const classList = Array.from(current.classList || []);\n if (classList.includes(\"react-flow\") || classList.includes(\"react-flow__pane\") || classList.includes(\"react-flow__viewport\")) {\n return true;\n }\n current = current.parentElement;\n }\n return false;\n }\n _getSelectorSafeElement(element) {\n var _a;\n let current = element;\n while (current) {\n const tagName = (_a = current.tagName) == null ? void 0 : _a.toLowerCase();\n if (current.namespaceURI === \"http://www.w3.org/1999/xhtml\") {\n return current;\n }\n if (current.namespaceURI === \"http://www.w3.org/2000/svg\") {\n current = current.parentElement;\n continue;\n }\n if (current.hasAttribute(\"data-testid\") || current.hasAttribute(\"data-item-id\") || current.hasAttribute(\"data-column-id\") || current.hasAttribute(\"draggable\")) {\n return current;\n }\n current = current.parentElement;\n }\n return element;\n }\n _isSliderThumb(element) {\n var _a, _b;\n const classes = typeof element.className === \"string\" ? element.className : ((_a = element.className) == null ? void 0 : _a.baseVal) || \"\";\n if (classes.includes(\"MuiSlider-mark\") || classes.includes(\"MuiSlider-markLabel\") || classes.includes(\"slider-label\") || classes.includes(\"slider-mark\"))\n return false;\n if (classes.includes(\"MuiSlider-thumb\"))\n return true;\n if (element.querySelector('input[type=\"range\"]'))\n return true;\n if (((_b = element.tagName) == null ? void 0 : _b.toLowerCase()) === \"input\" && element.type === \"range\")\n return true;\n if (classes.includes(\"slider-thumb\") || classes.includes(\"rc-slider-handle\") || classes.includes(\"noUi-handle\") || element.hasAttribute(\"role\") && element.getAttribute(\"role\") === \"slider\")\n return true;\n return false;\n }\n _findSliderThumb(element) {\n if (this._isSliderThumb(element))\n return element;\n if (element.parentElement && this._isSliderThumb(element.parentElement))\n return element.parentElement;\n return null;\n }\n _findSliderRoot(element) {\n var _a, _b;\n let current = element;\n for (let i = 0; i < 5 && current; i++) {\n const classes = typeof current.className === \"string\" ? current.className : ((_a = current.className) == null ? void 0 : _a.baseVal) || \"\";\n const tagName = ((_b = current.tagName) == null ? void 0 : _b.toLowerCase()) || \"\";\n if (tagName === \"input\" && current.type === \"range\")\n return current;\n if (classes.includes(\"MuiSlider-root\"))\n return current;\n if (classes.includes(\"rc-slider\") || classes.includes(\"noUi-target\") || classes.includes(\"slider-container\") || current.hasAttribute(\"role\") && current.getAttribute(\"role\") === \"slider\")\n return current;\n current = current.parentElement;\n }\n return null;\n }\n _shouldIgnoreForSlider(element) {\n var _a, _b;\n const classes = typeof element.className === \"string\" ? element.className : ((_a = element.className) == null ? void 0 : _a.baseVal) || \"\";\n const tagName = ((_b = element.tagName) == null ? void 0 : _b.toLowerCase()) || \"\";\n if (classes.includes(\"MuiSlider-markLabel\") || classes.includes(\"MuiSlider-mark\") || classes.includes(\"MuiSlider-valueLabel\") || classes.includes(\"slider-label\"))\n return true;\n if ((tagName === \"span\" || tagName === \"div\") && !classes.includes(\"MuiSlider-thumb\") && !classes.includes(\"slider-thumb\"))\n return true;\n return false;\n }\n _selectDraggableAncestor(element) {\n var _a;\n const selectors = [\n \"[data-item-id]\",\n // Specific to the kanban board items\n '[draggable=\"true\"]',\n \"[data-draggable]\",\n '[role=\"listitem\"]',\n \".draggable\",\n '[data-testid*=\"drag\"]'\n ];\n let current = element;\n const tagName = (_a = current.tagName) == null ? void 0 : _a.toLowerCase();\n if (tagName === \"button\" || tagName === \"input\" || tagName === \"select\" || tagName === \"textarea\" || tagName === \"a\") {\n current = current.parentElement;\n }\n for (let i = 0; i < 5 && current; i++) {\n if (current.hasAttribute(\"data-item-id\")) {\n return current;\n }\n if (selectors.some((sel) => {\n var _a2;\n return (_a2 = current == null ? void 0 : current.matches) == null ? void 0 : _a2.call(current, sel);\n })) {\n return current;\n }\n current = current.parentElement;\n }\n return element;\n }\n _getSourceColumn(element) {\n const sourceColumnId = element.getAttribute(\"data-source-column\");\n if (sourceColumnId) {\n const column = this._recorder.document.querySelector(`[data-column-id=\"${sourceColumnId}\"]`);\n if (column) return column;\n }\n let current = element;\n for (let i = 0; i < 10 && current; i++) {\n if (current.hasAttribute(\"data-column-id\")) {\n return current;\n }\n current = current.parentElement;\n }\n return null;\n }\n _selectDroppableAncestor(element) {\n let current = element;\n if (current.hasAttribute(\"data-item-id\") || current.hasAttribute(\"draggable\")) {\n current = current.parentElement;\n }\n for (let i = 0; i < 8 && current; i++) {\n if (current.hasAttribute(\"data-column-id\")) {\n return current;\n }\n if (current.hasAttribute(\"data-droppable\") && current.hasAttribute(\"data-testid\")) {\n const testId = current.getAttribute(\"data-testid\");\n if (testId && testId.startsWith(\"column-\")) {\n return current;\n }\n }\n if (current.hasAttribute(\"data-drop-target-for-element\") && current.hasAttribute(\"data-testid\")) {\n const testId = current.getAttribute(\"data-testid\");\n if (testId && testId.startsWith(\"calendar-cell-\")) {\n return current;\n }\n }\n current = current.parentElement;\n }\n current = element;\n if (current.hasAttribute(\"data-item-id\") || current.hasAttribute(\"draggable\")) {\n current = current.parentElement;\n }\n const fallbackSelectors = [\n \"[data-droppable]\",\n \"[data-drop-target-for-element]\",\n '[role=\"list\"]',\n '[role=\"listbox\"]',\n '[role=\"grid\"]',\n \".droppable\",\n \".drop-zone\",\n \"[data-drop-zone]\",\n // Library-specific drop-zone classes (SKYR-3706)\n \".vue-grid-layout\",\n \".react-grid-layout\",\n \"[data-rbd-droppable-id]\",\n \"[data-sortable]\"\n ];\n for (let i = 0; i < 8 && current; i++) {\n if (fallbackSelectors.some((sel) => {\n var _a;\n return (_a = current == null ? void 0 : current.matches) == null ? void 0 : _a.call(current, sel);\n })) {\n return current;\n }\n current = current.parentElement;\n }\n current = element;\n if (current.hasAttribute(\"data-item-id\") || current.hasAttribute(\"draggable\")) {\n current = current.parentElement;\n }\n for (let i = 0; i < 8 && current; i++) {\n if (current.querySelectorAll(':scope > [draggable=\"true\"]').length >= 2) {\n return current;\n }\n current = current.parentElement;\n }\n return element;\n }\n _relativePoint(el, clientX, clientY) {\n const r = el.getBoundingClientRect();\n return {\n x: Math.max(0, Math.min(clientX - r.left, r.width)),\n y: Math.max(0, Math.min(clientY - r.top, r.height))\n };\n }\n // SKYR-3706: Returns `#<id>` if the element has a stable-looking developer-chosen\n // id, otherwise null. Used to bypass Playwright's default selector generator for\n // drag/drop, which otherwise prefers text/role for unidentified divs and produces\n // brittle selectors like `internal:text=\"1行テキスト\"` for palette items that have\n // a perfectly good id like `#item_input`.\n //\n // Strict heuristic: id must start with a letter, contain no whitespace, and every\n // part (split on `_` or `-`) must be purely alphabetic. This accepts `item_input`,\n // `divRight`, `submit-button` but rejects framework-generated ids like\n // `mat-select-1234`, `section1_466`, `radix-r1`, `:r1:`, `elem-a3f9b2e1c0`. False\n // negatives are cheap (fall back to default selector); false positives are\n // expensive (selector breaks across runs).\n _stableIdSelector(element) {\n const id = element.getAttribute(\"id\");\n if (!id || !/^[a-zA-Z]/.test(id) || /\\s/.test(id))\n return null;\n if (!id.split(/[-_]/).every((part) => part.length > 0 && /^[a-zA-Z]+$/.test(part)))\n return null;\n return { selector: `#${id}` };\n }\n // SKYR-3706: When the drop target is a recognized drop-zone container\n // (vue-grid-layout, react-grid-layout), use the class as the selector\n // directly. Without this, Playwright's generateSelector picks the container's\n // accumulated innerText (every form item's label concatenated), which produces\n // an extremely brittle `internal:text=\"WF名 ※ WF期限 1行テキスト …\"` selector\n // that grows after each drop and only matches the exact previous-drop sequence.\n // Order matters: callers should try `_stableIdSelector` first (more specific),\n // then this. Only the canonical drop-zone classes are recognized — generic\n // utility classes like `sortable` are too common and would over-match.\n _stableContainerClassSelector(element) {\n const dropZoneClasses = [\"vue-grid-layout\", \"react-grid-layout\"];\n for (const cls of dropZoneClasses) {\n if (element.classList.contains(cls))\n return { selector: `.${cls}` };\n }\n return null;\n }\n // SKYR-3706: Returns a human-readable label for the drag source. Used by Skyramp\n // codegen to emit `expect(target).toContainText(label)` between consecutive drops\n // into the same drop zone, providing a settle signal that doesn't require a\n // network response. Priority: aria-label > textContent > title. innerText\n // beats title because tooltips are often generic (\"Drag to add\", \"Click to\n // edit\") and identical across siblings, while innerText is the discriminating\n // label of the specific item (\"1行テキスト\", \"チェックボックス\"). Returns empty\n // string if no usable label found (codegen falls back to a fixed delay).\n _extractSourceLabel(element) {\n const ariaLabel = element.getAttribute(\"aria-label\");\n if (ariaLabel && ariaLabel.trim())\n return ariaLabel.trim().slice(0, 80);\n const text = element.innerText || element.textContent || \"\";\n const trimmed = text.trim().replace(/\\s+/g, \" \");\n if (trimmed)\n return trimmed.slice(0, 80);\n const title = element.getAttribute(\"title\");\n if (title && title.trim())\n return title.trim().slice(0, 80);\n return \"\";\n }\n _isCenter(point, element) {\n const rect = element.getBoundingClientRect();\n const centerX = rect.width / 2;\n const centerY = rect.height / 2;\n const threshold = 5;\n return Math.abs(point.x - centerX) < threshold && Math.abs(point.y - centerY) < threshold;\n }\n _capture() {\n var _a, _b, _c, _d;\n if (!this._dragState || !this._dragState.source || !this._dragState.target) {\n return;\n }\n if (this._dragState.captured) {\n return;\n }\n this._dragState.captured = true;\n if (this._dragState.isReactFlow) {\n this._captureReactFlowDrag();\n return;\n }\n if (this._dragState.isGoJS) {\n this._captureGoJSDrag();\n return;\n }\n if (this._dragState.isCanvas) {\n this._captureCanvasDrag();\n return;\n }\n if (this._dragState.isSlider) {\n this._captureSliderDrag();\n return;\n }\n const sourceColumn = this._getSourceColumn(this._dragState.source);\n const targetColumn = this._dragState.target.hasAttribute(\"data-column-id\") ? this._dragState.target : this._getSourceColumn(this._dragState.target);\n const isSameColumn = sourceColumn && targetColumn && sourceColumn.getAttribute(\"data-column-id\") === targetColumn.getAttribute(\"data-column-id\");\n try {\n const sourceTestId = this._dragState.source.getAttribute(\"data-testid\") || this._dragState.source.getAttribute(\"data-item-id\");\n const targetTestId = this._dragState.target.getAttribute(\"data-testid\") || this._dragState.target.getAttribute(\"data-column-id\");\n let sourceGenerated;\n let targetGenerated;\n if (sourceTestId && targetTestId) {\n sourceGenerated = { selector: `[data-testid=\"${sourceTestId}\"]` };\n targetGenerated = { selector: `[data-testid=\"${targetTestId}\"]` };\n } else {\n const safeSource = this._getSelectorSafeElement(this._dragState.source);\n const safeTarget = this._getSelectorSafeElement(this._dragState.target);\n sourceGenerated = (_b = (_a = this._stableIdSelector(safeSource)) != null ? _a : this._stableContainerClassSelector(safeSource)) != null ? _b : this._recorder.injectedScript.generateSelector(safeSource, {\n testIdAttributeName: this._recorder.state.testIdAttributeName || \"data-testid\"\n });\n targetGenerated = (_d = (_c = this._stableIdSelector(safeTarget)) != null ? _c : this._stableContainerClassSelector(safeTarget)) != null ? _d : this._recorder.injectedScript.generateSelector(safeTarget, {\n testIdAttributeName: this._recorder.state.testIdAttributeName || \"data-testid\"\n });\n }\n const sourcePos = this._dragState.sourcePoint ? this._relativePoint(this._dragState.source, this._dragState.sourcePoint.x, this._dragState.sourcePoint.y) : { x: this._dragState.source.getBoundingClientRect().width / 2, y: this._dragState.source.getBoundingClientRect().height / 2 };\n const targetPos = this._dragState.targetPoint ? this._relativePoint(this._dragState.target, this._dragState.targetPoint.x, this._dragState.targetPoint.y) : { x: this._dragState.target.getBoundingClientRect().width / 2, y: this._dragState.target.getBoundingClientRect().height / 2 };\n const duration = Date.now() - this._dragState.startTime;\n const action = {\n name: \"dragTo\",\n selector: sourceGenerated.selector,\n target: targetGenerated.selector,\n signals: [],\n timestamp: getTimestamp(this._recorder)\n };\n if (!this._isCenter(sourcePos, this._dragState.source))\n action.sourcePosition = { x: Math.round(sourcePos.x), y: Math.round(sourcePos.y) };\n if (!this._isCenter(targetPos, this._dragState.target))\n action.targetPosition = { x: Math.round(targetPos.x), y: Math.round(targetPos.y) };\n if (duration > 500)\n action.duration = duration;\n const sourceLabel = this._extractSourceLabel(this._dragState.source);\n if (sourceLabel)\n action.sourceLabel = sourceLabel;\n this._recorder.recordAction(action);\n this._deactivate();\n } catch (error) {\n console.error(\"[PW-RECORDER] Error generating selectors:\", error);\n try {\n const sourceId = this._dragState.source.getAttribute(\"data-testid\") || this._dragState.source.getAttribute(\"data-item-id\");\n const targetId = this._dragState.target.getAttribute(\"data-testid\") || this._dragState.target.getAttribute(\"data-column-id\");\n if (sourceId && targetId) {\n const action = {\n name: \"dragTo\",\n selector: `[data-testid=\"${sourceId}\"]`,\n target: `[data-testid=\"${targetId}\"]`,\n signals: [],\n timestamp: getTimestamp(this._recorder)\n };\n const sourceLabel = this._extractSourceLabel(this._dragState.source);\n if (sourceLabel)\n action.sourceLabel = sourceLabel;\n this._recorder.recordAction(action);\n }\n } catch (fallbackError) {\n console.error(\"[PW-RECORDER] Fallback also failed:\", fallbackError);\n }\n this._deactivate();\n }\n this._dragState = {\n source: null,\n target: null,\n sourcePoint: null,\n targetPoint: null,\n startTime: Date.now(),\n isCanvas: false,\n isGoJS: false,\n isReactFlow: false,\n isSlider: false,\n dropDetected: false,\n captured: false\n };\n }\n /**\n * Helper to round position coordinates to ensure integer pixel values\n */\n _roundPosition(pos) {\n return { x: Math.round(pos.x), y: Math.round(pos.y) };\n }\n /**\n * Records a mouse drag operation as 4 separate mouse actions.\n *\n * This helper generates the sequence: mouse.move -> mouse.down -> mouse.move (with steps) -> mouse.up\n * which creates a realistic drag interaction. Used by both React Flow and slider drag operations\n * to generate low-level mouse actions instead of high-level dragTo operations.\n *\n * @param sourcePos - Starting position of the drag (viewport coordinates)\n * @param targetPos - Ending position of the drag (viewport coordinates)\n * @param steps - Number of intermediate steps for smooth movement (default: 10)\n */\n _recordMouseDragActions(sourcePos, targetPos, steps = 10) {\n const mouseMoveStart = {\n name: \"mouse.move\",\n position: this._roundPosition(sourcePos),\n signals: [],\n timestamp: getTimestamp(this._recorder)\n };\n this._recorder.recordAction(mouseMoveStart);\n const mouseDown = {\n name: \"mouse.down\",\n signals: [],\n timestamp: getTimestamp(this._recorder)\n };\n this._recorder.recordAction(mouseDown);\n const mouseMoveEnd = {\n name: \"mouse.move\",\n position: this._roundPosition(targetPos),\n steps,\n signals: [],\n timestamp: getTimestamp(this._recorder)\n };\n this._recorder.recordAction(mouseMoveEnd);\n const mouseUp = {\n name: \"mouse.up\",\n signals: [],\n timestamp: getTimestamp(this._recorder)\n };\n this._recorder.recordAction(mouseUp);\n }\n _captureReactFlowDrag() {\n if (!this._dragState || !this._dragState.source || !this._dragState.target) {\n return;\n }\n try {\n const reactFlowContainer = this._dragState.source;\n const containerRect = reactFlowContainer.getBoundingClientRect();\n const sourceAbsolutePos = this._dragState.sourcePoint ? { x: this._dragState.sourcePoint.x, y: this._dragState.sourcePoint.y } : { x: containerRect.left + containerRect.width / 2, y: containerRect.top + containerRect.height / 2 };\n const targetAbsolutePos = this._dragState.targetPoint ? { x: this._dragState.targetPoint.x, y: this._dragState.targetPoint.y } : { x: containerRect.left + containerRect.width / 2, y: containerRect.top + containerRect.height / 2 };\n this._recordMouseDragActions(sourceAbsolutePos, targetAbsolutePos, 10);\n } catch (error) {\n console.error(\"[PW-RECORDER] Error capturing React Flow drag:\", error);\n }\n this._deactivate();\n this._dragState = {\n source: null,\n target: null,\n sourcePoint: null,\n targetPoint: null,\n startTime: Date.now(),\n isCanvas: false,\n isGoJS: false,\n isReactFlow: false,\n isSlider: false,\n dropDetected: false,\n captured: false\n };\n }\n _captureCanvasDrag() {\n if (!this._dragState || !this._dragState.source || !this._dragState.target) {\n return;\n }\n try {\n const canvasElement = this._dragState.source;\n const sourceGenerated = this._recorder.injectedScript.generateSelector(canvasElement, {\n testIdAttributeName: this._recorder.state.testIdAttributeName || \"data-testid\"\n });\n const sourcePos = this._dragState.sourcePoint ? this._relativePoint(canvasElement, this._dragState.sourcePoint.x, this._dragState.sourcePoint.y) : { x: canvasElement.getBoundingClientRect().width / 2, y: canvasElement.getBoundingClientRect().height / 2 };\n const targetPos = this._dragState.targetPoint ? this._relativePoint(canvasElement, this._dragState.targetPoint.x, this._dragState.targetPoint.y) : { x: canvasElement.getBoundingClientRect().width / 2, y: canvasElement.getBoundingClientRect().height / 2 };\n const duration = Date.now() - this._dragState.startTime;\n const action = {\n name: \"dragTo\",\n selector: sourceGenerated.selector,\n target: sourceGenerated.selector,\n sourcePosition: { x: Math.round(sourcePos.x), y: Math.round(sourcePos.y) },\n targetPosition: { x: Math.round(targetPos.x), y: Math.round(targetPos.y) },\n signals: [],\n timestamp: getTimestamp(this._recorder)\n };\n if (duration > 500)\n action.duration = duration;\n this._recorder.recordAction(action);\n } catch (error) {\n console.error(\"[PW-RECORDER] Error capturing canvas drag:\", error);\n }\n this._deactivate();\n this._dragState = {\n source: null,\n target: null,\n sourcePoint: null,\n targetPoint: null,\n startTime: Date.now(),\n isCanvas: false,\n isGoJS: false,\n isReactFlow: false,\n isSlider: false,\n dropDetected: false,\n captured: false\n };\n }\n _captureSliderDrag() {\n if (!this._dragState || !this._dragState.source || !this._dragState.sourcePoint || !this._dragState.targetPoint) {\n return;\n }\n try {\n const sourcePos = {\n x: Math.round(this._dragState.sourcePoint.x),\n y: Math.round(this._dragState.sourcePoint.y)\n };\n const targetPos = {\n x: Math.round(this._dragState.targetPoint.x),\n y: Math.round(this._dragState.targetPoint.y)\n };\n const dx = targetPos.x - sourcePos.x;\n const dy = targetPos.y - sourcePos.y;\n const distance = Math.sqrt(dx * dx + dy * dy);\n const steps = Math.max(1, Math.floor(distance / _DragDropTool.PIXELS_PER_STEP));\n const sliderElement = this._dragState.source;\n let sliderInfo = \"\";\n let direction = \"\";\n if (Math.abs(dx) > Math.abs(dy)) {\n direction = dx > 0 ? \"right\" : \"left\";\n } else {\n direction = dy > 0 ? \"down\" : \"up\";\n }\n const getSliderInfo = (element, visited = /* @__PURE__ */ new Set()) => {\n if (visited.has(element)) {\n return null;\n }\n visited.add(element);\n const ariaValue = element.getAttribute(\"aria-valuenow\");\n const ariaMin = element.getAttribute(\"aria-valuemin\");\n const ariaMax = element.getAttribute(\"aria-valuemax\");\n if (ariaValue) {\n return {\n value: ariaValue,\n min: ariaMin || void 0,\n max: ariaMax || void 0\n };\n }\n if (element instanceof HTMLInputElement && element.type === \"range\") {\n return {\n value: element.value,\n min: element.min || void 0,\n max: element.max || void 0\n };\n }\n const children = Array.from(element.children);\n for (const child of children) {\n if (!visited.has(child)) {\n if (child instanceof HTMLInputElement && child.type === \"range\" || child.hasAttribute(\"aria-valuenow\")) {\n const result = getSliderInfo(child, visited);\n if (result) return result;\n }\n }\n }\n if (element.parentElement) {\n const siblings = Array.from(element.parentElement.children);\n for (const sibling of siblings) {\n if (sibling !== element && !visited.has(sibling)) {\n if (sibling instanceof HTMLInputElement && sibling.type === \"range\" || sibling.hasAttribute(\"aria-valuenow\")) {\n const result = getSliderInfo(sibling, visited);\n if (result) return result;\n }\n }\n }\n if (!visited.has(element.parentElement)) {\n const parentAriaValue = element.parentElement.getAttribute(\"aria-valuenow\");\n if (parentAriaValue) {\n return {\n value: parentAriaValue,\n min: element.parentElement.getAttribute(\"aria-valuemin\") || void 0,\n max: element.parentElement.getAttribute(\"aria-valuemax\") || void 0\n };\n }\n }\n }\n return null;\n };\n const sliderData = getSliderInfo(sliderElement);\n if (sliderData && sliderData.value) {\n const rangeInfo = sliderData.min && sliderData.max ? ` (range: ${sliderData.min} to ${sliderData.max})` : \"\";\n sliderInfo = ` to value ${sliderData.value}${rangeInfo}`;\n }\n const commentAction = {\n name: \"comment\",\n text: `Moving slider ${direction}${sliderInfo}`,\n signals: [],\n timestamp: getTimestamp(this._recorder)\n };\n this._recorder.recordAction(commentAction);\n this._recordMouseDragActions(sourcePos, targetPos, steps);\n } catch (error) {\n console.error(\"[PW-RECORDER] Error capturing slider drag:\", error);\n }\n this._deactivate();\n this._dragState = {\n source: null,\n target: null,\n sourcePoint: null,\n targetPoint: null,\n startTime: Date.now(),\n isCanvas: false,\n isGoJS: false,\n isReactFlow: false,\n isSlider: false,\n dropDetected: false,\n captured: false\n };\n }\n};\n// Configuration constants for smooth mouse movements\n_DragDropTool.PIXELS_PER_STEP = 5;\n// ~5px per step for smooth slider dragging\n_DragDropTool.WHEEL_DEBOUNCE_MS = 500;\n// Wait 500ms after last wheel event before recording\n_DragDropTool.WHEEL_MAX_ACCUMULATION_MS = 1e3;\n// Max time to accumulate before forcing a record\n_DragDropTool.WHEEL_TOOL_DISABLE_MS = 1e3;\n// Wait 1000ms after last wheel event before disabling DD tool\n_DragDropTool.WHEEL_SCROLL_TIMEOUT_MS = 3e3;\n// Wait after scroll block comment to let the page settle\n// Mac Magic Mouse / trackpad touch surfaces generate stray wheel events\n// whenever a finger drags across them — even when the user is just moving\n// the cursor, not scrolling. Those flutter sequences typically accumulate\n// to small bidirectional deltas (both |deltaX| and |deltaY| under ~30px,\n// often with mixed signs). Recording them produces synthesized\n// page.mouse.wheel() calls on replay that scroll real content under the\n// pointer and can move the next click target out of view.\n//\n// Filter rule at flush time: if neither accumulated axis crossed this\n// threshold, treat the sequence as Magic Mouse noise and don't emit the\n// mouse.wheel action. The mouse.move/comment/waitForTimeout actions that\n// were emitted at the start of the sequence stay in the trace; they are\n// harmless on replay (cursor move + 3s wait) and removing them would\n// require buffering — out of scope for this filter.\n_DragDropTool.WHEEL_NOISE_AXIS_THRESHOLD = 30;\nvar DragDropTool = _DragDropTool;\n\n// packages/injected/src/recorder/skyramp/gojsLinkTool.ts\nfunction getTimestamp2(recorder) {\n return recorder.injectedScript.utils.builtins.Date.now().toString();\n}\nvar GoJSLinkTool = class {\n constructor(recorder) {\n this._diagramEntries = [];\n this._keydownRemover = null;\n this._recorder = recorder;\n }\n cursor() {\n return \"crosshair\";\n }\n install() {\n console.log(\"[GoJSLinkTool] install() \\u2014 document:\", this._recorder.document.URL);\n this._recorder.document.__skyrampGojsLinkToolActive = true;\n this._diagramEntries = [];\n const canvases = Array.from(this._recorder.document.querySelectorAll(\"canvas\"));\n console.log(\"[GoJSLinkTool] found canvases:\", canvases.length);\n for (const canvas of canvases) {\n const entry = this._hookDiagram(canvas);\n if (entry) {\n this._diagramEntries.push(entry);\n console.log(\"[GoJSLinkTool] hooked diagram, panelSelector:\", entry.panelSelector);\n }\n }\n console.log(\"[GoJSLinkTool] hooked\", this._diagramEntries.length, \"diagram(s)\");\n const onKeyDown = (e) => {\n if (e.key === \"Escape\") {\n this._recorder.setMode(\"recording\");\n e.preventDefault();\n e.stopPropagation();\n }\n };\n this._recorder.document.addEventListener(\"keydown\", onKeyDown, true);\n this._keydownRemover = () => this._recorder.document.removeEventListener(\"keydown\", onKeyDown, true);\n }\n uninstall() {\n var _a;\n console.log(\"[GoJSLinkTool] uninstall() \\u2014 document:\", this._recorder.document.URL);\n delete this._recorder.document.__skyrampGojsLinkToolActive;\n for (const entry of this._diagramEntries) {\n try {\n entry.diagram.allowMove = entry.prevAllowMove;\n const lt = (_a = entry.diagram.toolManager) == null ? void 0 : _a.linkingTool;\n if (lt) lt.isEnabled = entry.prevLinkingEnabled;\n entry.diagram.removeDiagramListener(\"LinkDrawn\", entry.linkDrawnHandler);\n entry.diagram.removeDiagramListener(\"ExternalObjectsDropped\", entry.externalDropHandler);\n console.log(\"[GoJSLinkTool] restored diagram, panelSelector:\", entry.panelSelector);\n } catch (_e) {\n console.log(\"[GoJSLinkTool] error restoring diagram:\", _e);\n }\n }\n this._diagramEntries = [];\n if (this._keydownRemover) {\n this._keydownRemover();\n this._keydownRemover = null;\n }\n }\n _hookDiagram(canvas) {\n var _a, _b, _c, _d, _e, _f;\n const win = (_a = canvas.ownerDocument) == null ? void 0 : _a.defaultView;\n if (!((_c = (_b = win == null ? void 0 : win.go) == null ? void 0 : _b.Diagram) == null ? void 0 : _c.fromDiv))\n return null;\n let el = canvas.parentElement;\n while (el && el !== canvas.ownerDocument.body) {\n const diagram = win.go.Diagram.fromDiv(el);\n if (diagram) {\n const panelSelector = this._buildSelector(el);\n const prevAllowMove = diagram.allowMove;\n diagram.allowMove = false;\n const lt = (_d = diagram.toolManager) == null ? void 0 : _d.linkingTool;\n const prevLinkingEnabled = (_e = lt == null ? void 0 : lt.isEnabled) != null ? _e : true;\n if (lt) {\n lt.isEnabled = true;\n if (((_f = lt.portGravity) != null ? _f : 0) < 10)\n lt.portGravity = 10;\n }\n const linkDrawnHandler = (e) => {\n var _a2, _b2, _c2, _d2;\n const link = e.subject;\n if (!(link == null ? void 0 : link.data)) return;\n const fromKey = String((_a2 = link.data.from) != null ? _a2 : \"\");\n const toKey = String((_b2 = link.data.to) != null ? _b2 : \"\");\n if (!fromKey || !toKey) return;\n console.log(\"[GoJSLinkTool] LinkDrawn from:\", fromKey, \"to:\", toKey);\n this._emitDiagramLinkAdd(\n fromKey,\n toKey,\n String((_c2 = link.data.fromPort) != null ? _c2 : \"\"),\n String((_d2 = link.data.toPort) != null ? _d2 : \"\"),\n panelSelector\n );\n };\n diagram.addDiagramListener(\"LinkDrawn\", linkDrawnHandler);\n const externalDropHandler = (e) => {\n e.subject.each((part) => {\n var _a2, _b2;\n if (!(part == null ? void 0 : part.data)) return;\n if (part.data.from !== void 0) return;\n const category = String((_a2 = part.data.category) != null ? _a2 : \"\");\n const key = String((_b2 = part.data.key) != null ? _b2 : \"\");\n const loc = part.location;\n console.log(\"[GoJSLinkTool] ExternalObjectsDropped category:\", category, \"key:\", key, \"loc:\", loc == null ? void 0 : loc.x, loc == null ? void 0 : loc.y);\n setTimeout(() => {\n var _a3, _b3, _c2, _d2, _e2, _f2;\n this._emitDiagramNodeAdd(diagram, panelSelector, category, key, (_c2 = (_b3 = (_a3 = part.location) == null ? void 0 : _a3.x) != null ? _b3 : loc == null ? void 0 : loc.x) != null ? _c2 : 0, (_f2 = (_e2 = (_d2 = part.location) == null ? void 0 : _d2.y) != null ? _e2 : loc == null ? void 0 : loc.y) != null ? _f2 : 0);\n }, 0);\n });\n };\n diagram.addDiagramListener(\"ExternalObjectsDropped\", externalDropHandler);\n return { diagram, panelSelector, prevAllowMove, prevLinkingEnabled, linkDrawnHandler, externalDropHandler };\n }\n el = el.parentElement;\n }\n return null;\n }\n _emitDiagramLinkAdd(fromKey, toKey, fromPort, toPort, panelSelector) {\n var _a;\n const action = {\n name: \"diagramLinkAdd\",\n diagramType: \"gojs\",\n panelSelector,\n fromKey,\n toKey,\n fromPort,\n toPort,\n signals: [],\n timestamp: getTimestamp2(this._recorder)\n };\n this._recorder.recordAction(action);\n (_a = this._recorder.overlay) == null ? void 0 : _a.flashToolSucceeded(\"recordingGoJSLink\");\n }\n /**\n * Emit a diagramNodeAdd action for a palette → canvas drop.\n * Computes anchorKey, anchorOffsetX/Y, and anchorDocX/Y by finding the nearest\n * existing node in the diagram to serve as a stable reference point. Anchor\n * candidates include all nodes currently in the diagram — both pre-existing nodes\n * and any nodes added earlier in this recording session.\n */\n _emitDiagramNodeAdd(diagram, targetPanelSelector, category, key, docX, docY) {\n var _a, _b;\n const paletteEntry = this._diagramEntries.find((entry) => {\n var _a2, _b2, _c;\n try {\n const win = (_b2 = (_a2 = entry.diagram.div) == null ? void 0 : _a2.ownerDocument) == null ? void 0 : _b2.defaultView;\n return ((_c = win == null ? void 0 : win.go) == null ? void 0 : _c.Palette) && entry.diagram instanceof win.go.Palette;\n } catch (_) {\n return false;\n }\n });\n const sourcePanelSelector = (_a = paletteEntry == null ? void 0 : paletteEntry.panelSelector) != null ? _a : \"\";\n let anchorKey;\n let anchorOffsetX;\n let anchorOffsetY;\n let anchorDocX;\n let anchorDocY;\n let minDist = Infinity;\n diagram.nodes.each((node) => {\n var _a2;\n if (!(node == null ? void 0 : node.data)) return;\n const nKey = String((_a2 = node.data.key) != null ? _a2 : \"\");\n if (!nKey || nKey === key) return;\n const dx = node.location.x - docX;\n const dy = node.location.y - docY;\n const dist = Math.sqrt(dx * dx + dy * dy);\n if (dist < minDist) {\n minDist = dist;\n anchorKey = nKey;\n anchorOffsetX = Math.round(docX - node.location.x);\n anchorOffsetY = Math.round(docY - node.location.y);\n anchorDocX = Math.round(node.location.x);\n anchorDocY = Math.round(node.location.y);\n }\n });\n const action = {\n name: \"diagramNodeAdd\",\n diagramType: \"gojs\",\n sourcePanelSelector,\n targetPanelSelector,\n sourceIsPalette: true,\n targetIsPalette: false,\n sourceCategory: category,\n sourceKey: key,\n targetDocX: Math.round(docX),\n targetDocY: Math.round(docY),\n anchorKey,\n anchorOffsetX,\n anchorOffsetY,\n anchorDocX,\n anchorDocY,\n signals: [],\n timestamp: getTimestamp2(this._recorder)\n };\n this._recorder.recordAction(action);\n (_b = this._recorder.overlay) == null ? void 0 : _b.flashToolSucceeded(\"recordingGoJSLink\");\n }\n _buildSelector(el) {\n if (el.id)\n return `#${el.id}`;\n if (el.getAttribute(\"data-testid\"))\n return `[data-testid=\"${el.getAttribute(\"data-testid\")}\"]`;\n const parent = el.parentElement;\n if (parent) {\n const idx = Array.from(parent.children).indexOf(el) + 1;\n return `${el.tagName.toLowerCase()}:nth-child(${idx})`;\n }\n return el.tagName.toLowerCase();\n }\n};\n\n// packages/injected/src/recorder/skyramp/fileUploadTool.ts\nfunction consumeEvent(e) {\n e.preventDefault();\n e.stopPropagation();\n e.stopImmediatePropagation();\n}\nfunction getTimestamp3(recorder) {\n return recorder.injectedScript.utils.builtins.Date.now().toString();\n}\nvar FileUploadTool = class {\n constructor(recorder) {\n this._triggerElement = null;\n // Cached at arm time so the selector is generated against the LIVE, still-\n // attached trigger element (typically the menuitem the user clicked). By\n // resolved-time the menuitem is often detached — generateSelector then\n // throws inside cssFallback/parseSelectorString and the entire resolved\n // handler aborts before recording the action. Caching here makes the\n // recorded action robust to that detachment.\n this._triggerSelector = null;\n this._input = null;\n this._pendingFiles = [];\n this._helperOverlay = null;\n this._recorder = recorder;\n }\n cursor() {\n return \"pointer\";\n }\n install() {\n const win = this._recorder.injectedScript.window;\n win.__pwRecorderFileChooserArmed = (event) => {\n if (event.input && event.triggerElement === event.input) {\n return;\n }\n this._triggerElement = event.triggerElement;\n this._input = event.input;\n this._triggerSelector = event.triggerSelector || null;\n if (!this._triggerSelector && event.triggerElement) {\n try {\n this._triggerSelector = this._recorder.injectedScript.generateSelector(event.triggerElement, {\n testIdAttributeName: this._recorder.state.testIdAttributeName,\n multiple: false\n }).selector;\n } catch (e) {\n console.warn(\"[FileUploadTool] arm-time selector fallback failed:\", e);\n }\n }\n this._showHelperOverlay(\"File chooser opening... Please select a file\");\n };\n win.__pwRecorderFileChooserResolved = (event) => {\n var _a;\n if (event.input && !this._triggerElement) {\n return;\n }\n this._pendingFiles = event.files;\n this._hideHelperOverlay();\n if (this._triggerElement && event.files.length > 0) {\n const filePaths = event.files.map((f) => f.name);\n let selector = this._triggerSelector;\n if (!selector) {\n try {\n selector = this._recorder.injectedScript.generateSelector(this._triggerElement, {\n testIdAttributeName: this._recorder.state.testIdAttributeName,\n multiple: false\n }).selector;\n } catch (e) {\n console.warn(\"[FileUploadTool] resolved-time selector generation failed:\", e);\n }\n }\n if (selector) {\n const action = {\n name: \"fileChooser\",\n selector,\n files: filePaths,\n signals: [],\n timestamp: getTimestamp3(this._recorder)\n };\n this._recorder.recordAction(action);\n this._recorder.setMode(\"recording\");\n (_a = this._recorder.overlay) == null ? void 0 : _a.flashToolSucceeded(\"fileUpload\");\n } else {\n console.warn(\"[FileUploadTool] no selector available for trigger element; action not recorded\");\n }\n } else {\n console.warn(\"[FileUploadTool] Missing trigger element or no files selected\");\n }\n };\n this._showHelperOverlay(\"Click a button to upload a file\");\n }\n uninstall() {\n const win = this._recorder.injectedScript.window;\n win.__pwRecorderFileChooserArmed = void 0;\n win.__pwRecorderFileChooserResolved = void 0;\n this._hideHelperOverlay();\n this._triggerElement = null;\n this._triggerSelector = null;\n this._input = null;\n this._pendingFiles = [];\n }\n onClick(event) {\n const recordTool = this._getRecordActionTool();\n if (!recordTool || !recordTool.onClick) {\n return;\n }\n recordTool.onClick(event);\n }\n onInput(event) {\n var _a;\n const target = this._recorder.deepEventTarget(event);\n if (target.nodeName === \"INPUT\" && target.type.toLowerCase() === \"file\") {\n if (this._triggerElement) {\n return;\n }\n const inputElement = target;\n const generated = this._recorder.injectedScript.generateSelector(inputElement, {\n testIdAttributeName: this._recorder.state.testIdAttributeName,\n multiple: false\n });\n this._recorder.recordAction({\n name: \"setInputFiles\",\n selector: generated.selector,\n signals: [],\n files: [...inputElement.files || []].map((file) => file.name),\n timestamp: getTimestamp3(this._recorder)\n });\n this._recorder.setMode(\"recording\");\n (_a = this._recorder.overlay) == null ? void 0 : _a.flashToolSucceeded(\"fileUpload\");\n } else {\n const recordTool = this._getRecordActionTool();\n if (recordTool && recordTool.onInput) {\n recordTool.onInput(event);\n }\n }\n }\n onKeyDown(event) {\n if (event.key === \"Escape\") {\n consumeEvent(event);\n this._recorder.setMode(\"recording\");\n return;\n }\n const recordTool = this._getRecordActionTool();\n if (recordTool && recordTool.onKeyDown) {\n recordTool.onKeyDown(event);\n }\n }\n onKeyUp(event) {\n const recordTool = this._getRecordActionTool();\n if (recordTool && recordTool.onKeyUp) {\n recordTool.onKeyUp(event);\n }\n }\n onPointerDown(event) {\n const recordTool = this._getRecordActionTool();\n if (recordTool && recordTool.onPointerDown) {\n recordTool.onPointerDown(event);\n }\n }\n onPointerUp(event) {\n const recordTool = this._getRecordActionTool();\n if (recordTool && recordTool.onPointerUp) {\n recordTool.onPointerUp(event);\n }\n }\n onPointerMove(event) {\n const recordTool = this._getRecordActionTool();\n if (recordTool && recordTool.onPointerMove) {\n recordTool.onPointerMove(event);\n }\n }\n onMouseMove(event) {\n const recordTool = this._getRecordActionTool();\n if (recordTool && recordTool.onMouseMove) {\n recordTool.onMouseMove(event);\n }\n }\n onMouseDown(event) {\n const recordTool = this._getRecordActionTool();\n if (recordTool && recordTool.onMouseDown) {\n recordTool.onMouseDown(event);\n }\n }\n onMouseUp(event) {\n const recordTool = this._getRecordActionTool();\n if (recordTool && recordTool.onMouseUp) {\n recordTool.onMouseUp(event);\n }\n }\n onMouseLeave(event) {\n const recordTool = this._getRecordActionTool();\n if (recordTool && recordTool.onMouseLeave) {\n recordTool.onMouseLeave(event);\n }\n }\n onFocus(event) {\n const recordTool = this._getRecordActionTool();\n if (recordTool && recordTool.onFocus) {\n recordTool.onFocus(event);\n }\n }\n _getRecordActionTool() {\n var _a;\n return ((_a = this._recorder._tools) == null ? void 0 : _a[\"recording\"]) || null;\n }\n _showHelperOverlay(message) {\n this._hideHelperOverlay();\n const overlay = this._recorder.document.createElement(\"div\");\n overlay.style.cssText = `\n position: fixed;\n top: 20px;\n left: 50%;\n transform: translateX(-50%);\n background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);\n color: white;\n padding: 12px 24px;\n border-radius: 8px;\n box-shadow: 0 4px 12px rgba(0, 0, 0, 0.3);\n z-index: 2147483645;\n font-family: system-ui, -apple-system, sans-serif;\n font-size: 14px;\n font-weight: 500;\n pointer-events: none;\n animation: pw-slide-down 0.3s ease-out;\n `;\n overlay.textContent = `\\u{1F4CE} ${message}`;\n const style = this._recorder.document.createElement(\"style\");\n style.textContent = `\n @keyframes pw-slide-down {\n from {\n opacity: 0;\n transform: translateX(-50%) translateY(-20px);\n }\n to {\n opacity: 1;\n transform: translateX(-50%) translateY(0);\n }\n }\n `;\n this._recorder.document.head.appendChild(style);\n this._recorder.document.body.appendChild(overlay);\n this._helperOverlay = overlay;\n }\n _hideHelperOverlay() {\n if (this._helperOverlay) {\n this._helperOverlay.remove();\n this._helperOverlay = null;\n }\n }\n};\n\n// packages/injected/src/recorder/skyramp/fileUploadHooks.ts\nfunction addEventListener2(target, eventName, listener, useCapture) {\n target.addEventListener(eventName, listener, useCapture);\n const remove = () => {\n target.removeEventListener(eventName, listener, useCapture);\n };\n return remove;\n}\nfunction installFileUploadHooks(recorder, listeners) {\n const win = recorder.injectedScript.window;\n const doc = recorder.document;\n let lastClickedElement = null;\n let lastClickedSelector = null;\n let lastClickTimestamp = 0;\n const instrumentedInputs = /* @__PURE__ */ new WeakSet();\n listeners.push(\n addEventListener2(doc, \"click\", (e) => {\n var _a;\n const event = e;\n const target = recorder.deepEventTarget(event);\n if (!event.isTrusted)\n return;\n if (target.nodeName === \"INPUT\" && target.type.toLowerCase() === \"file\")\n return;\n lastClickedElement = target;\n lastClickTimestamp = Date.now();\n try {\n lastClickedSelector = recorder.injectedScript.generateSelector(target, {\n testIdAttributeName: (_a = recorder.state) == null ? void 0 : _a.testIdAttributeName,\n multiple: false\n }).selector;\n } catch (err) {\n console.warn(\"[PW-FileUpload] click-capture selector generation failed:\", err);\n lastClickedSelector = null;\n }\n }, true)\n );\n const instrumentFileInput = (input) => {\n if (instrumentedInputs.has(input)) {\n return;\n }\n if (input.type !== \"file\") {\n return;\n }\n instrumentedInputs.add(input);\n const originalClick = input.click;\n input.click = function() {\n if (win.__pwRecorderFileChooserArmed) {\n win.__pwRecorderFileChooserArmed({\n triggerElement: lastClickedElement,\n triggerSelector: lastClickedSelector,\n input: this,\n timestamp: lastClickTimestamp\n });\n }\n return originalClick.apply(this, arguments);\n };\n addEventListener2(input, \"change\", () => {\n const files = Array.from(input.files || []).map((f) => ({\n name: f.name,\n size: f.size,\n type: f.type,\n lastModified: f.lastModified\n }));\n if (win.__pwRecorderFileChooserResolved && files.length > 0) {\n win.__pwRecorderFileChooserResolved({\n files,\n input\n });\n }\n }, true);\n };\n const originalInputClick = HTMLInputElement.prototype.click;\n HTMLInputElement.prototype.click = function() {\n if (this.type === \"file\") {\n instrumentFileInput(this);\n if (win.__pwRecorderFileChooserArmed) {\n win.__pwRecorderFileChooserArmed({\n triggerElement: lastClickedElement,\n triggerSelector: lastClickedSelector,\n input: this,\n timestamp: lastClickTimestamp\n });\n }\n }\n return originalInputClick.apply(this, arguments);\n };\n const originalCreateElement = Document.prototype.createElement;\n Document.prototype.createElement = function(tagName, options) {\n const element = originalCreateElement.call(this, tagName, options);\n if (element instanceof HTMLInputElement && element.type === \"file\") {\n instrumentFileInput(element);\n }\n return element;\n };\n const typeDescriptor = Object.getOwnPropertyDescriptor(HTMLInputElement.prototype, \"type\");\n if (typeDescriptor && typeDescriptor.set) {\n const originalTypeSetter = typeDescriptor.set;\n Object.defineProperty(HTMLInputElement.prototype, \"type\", {\n ...typeDescriptor,\n set(value) {\n const result = originalTypeSetter.call(this, value);\n if (String(value).toLowerCase() === \"file\") {\n instrumentFileInput(this);\n }\n return result;\n }\n });\n }\n if (\"showOpenFilePicker\" in win) {\n const originalShowOpenFilePicker = win.showOpenFilePicker;\n win.showOpenFilePicker = async function(...args) {\n if (win.__pwRecorderFileChooserArmed) {\n win.__pwRecorderFileChooserArmed({\n triggerElement: lastClickedElement,\n triggerSelector: lastClickedSelector,\n input: null,\n timestamp: lastClickTimestamp\n });\n }\n const handles = await originalShowOpenFilePicker.apply(this, args);\n const files = [];\n try {\n for (const handle of handles) {\n const file = await handle.getFile();\n files.push({\n name: file.name,\n size: file.size,\n type: file.type,\n lastModified: file.lastModified\n });\n }\n } catch (e) {\n console.warn(\"[PW-FileUpload] Failed to extract file metadata from handles:\", e);\n for (const handle of handles) {\n files.push({ name: handle.name || \"unknown\" });\n }\n }\n if (win.__pwRecorderFileChooserResolved && files.length > 0) {\n win.__pwRecorderFileChooserResolved({\n files,\n input: null\n });\n }\n return handles;\n };\n }\n const existingInputs = doc.querySelectorAll('input[type=\"file\"]');\n existingInputs.forEach((input) => {\n instrumentFileInput(input);\n });\n const observer = new MutationObserver((mutations) => {\n for (const mutation of mutations) {\n for (const node of mutation.addedNodes) {\n if (node instanceof HTMLInputElement && node.type === \"file\") {\n instrumentFileInput(node);\n }\n if (node instanceof Element) {\n const inputs = node.querySelectorAll('input[type=\"file\"]');\n inputs.forEach((input) => {\n instrumentFileInput(input);\n });\n }\n }\n }\n });\n observer.observe(doc.documentElement, { childList: true, subtree: true });\n listeners.push(() => {\n observer.disconnect();\n });\n}\n\n// packages/injected/src/recorder/skyramp/sketchTool.ts\nfunction getTimestamp4(recorder) {\n return recorder.injectedScript.utils.builtins.Date.now().toString();\n}\nvar SketchTool = class {\n // Minimum pixels between points\n constructor(recorder) {\n this._isDeleting = false;\n this._viewportPath = [];\n // Viewport coordinates (clientX, clientY)\n this._lastPointTime = 0;\n this.POINT_THROTTLE_MS = 33;\n // ~30fps\n this.MIN_DISTANCE = 5;\n this._recorder = recorder;\n }\n cursor() {\n return \"pointer\";\n }\n install() {\n var _a;\n (_a = this._recorder.injectedScript.document.body) == null ? void 0 : _a.classList.add(\"pw-sketch-tool-cursor\");\n this._createOverlayCanvas();\n }\n uninstall() {\n var _a;\n (_a = this._recorder.injectedScript.document.body) == null ? void 0 : _a.classList.remove(\"pw-sketch-tool-cursor\");\n this._removeOverlayCanvas();\n this._isDeleting = false;\n this._viewportPath = [];\n }\n onPointerDown(event) {\n if (event.button !== 0)\n return;\n const target = this._recorder.deepEventTarget(event);\n this._isDeleting = true;\n this._viewportPath = [];\n this._addPoint(event);\n this._clearOverlayPath();\n this._dispatchRealMouseEvent(\"mousedown\", event, target);\n return true;\n }\n onPointerMove(event) {\n if (!this._isDeleting)\n return;\n const target = this._recorder.deepEventTarget(event);\n this._dispatchRealMouseEvent(\"mousemove\", event, target);\n const now = this._recorder.injectedScript.utils.builtins.Date.now();\n if (now - this._lastPointTime < this.POINT_THROTTLE_MS)\n return;\n if (this._viewportPath.length > 0) {\n const lastPoint = this._viewportPath[this._viewportPath.length - 1];\n const distance = Math.hypot(\n event.clientX - lastPoint.x,\n event.clientY - lastPoint.y\n );\n if (distance >= this.MIN_DISTANCE) {\n this._addPoint(event);\n this._lastPointTime = now;\n this._updateOverlayPath();\n }\n }\n }\n onPointerUp(event) {\n var _a;\n if (!this._isDeleting)\n return;\n const target = this._recorder.deepEventTarget(event);\n this._dispatchRealMouseEvent(\"mouseup\", event, target);\n this._isDeleting = false;\n if (this._viewportPath.length > 1) {\n this._addPoint(event);\n this._recordSketchToolAsMouseActions();\n (_a = this._recorder.overlay) == null ? void 0 : _a.flashToolSucceeded(\"recordingSketchTool\");\n }\n this._clearOverlayPath();\n this._viewportPath = [];\n }\n onMouseDown(event) {\n return;\n }\n onMouseUp(event) {\n return;\n }\n onClick(event) {\n return;\n }\n _addPoint(event) {\n this._viewportPath.push({\n x: Math.round(event.clientX),\n y: Math.round(event.clientY)\n });\n }\n _recordSketchToolAsMouseActions() {\n const optimizedPath = this._optimizePath(this._viewportPath);\n if (optimizedPath.length === 0)\n return;\n const commentAction = {\n name: \"comment\",\n text: `Sketch tool with ${optimizedPath.length} path points`,\n signals: [],\n timestamp: getTimestamp4(this._recorder)\n };\n this._recorder.recordAction(commentAction);\n const firstPoint = optimizedPath[0];\n const mouseMoveToStart = {\n name: \"mouse.move\",\n position: firstPoint,\n signals: [],\n timestamp: getTimestamp4(this._recorder)\n };\n this._recorder.recordAction(mouseMoveToStart);\n const mouseDown = {\n name: \"mouse.down\",\n signals: [],\n timestamp: getTimestamp4(this._recorder)\n };\n this._recorder.recordAction(mouseDown);\n for (let i = 1; i < optimizedPath.length; i++) {\n const point = optimizedPath[i];\n const prevPoint = optimizedPath[i - 1];\n const dx = point.x - prevPoint.x;\n const dy = point.y - prevPoint.y;\n const distance = Math.sqrt(dx * dx + dy * dy);\n const steps = Math.max(1, Math.floor(distance / 5));\n const mouseMoveAction = {\n name: \"mouse.move\",\n position: point,\n steps,\n signals: [],\n timestamp: getTimestamp4(this._recorder)\n };\n this._recorder.recordAction(mouseMoveAction);\n }\n const mouseUp = {\n name: \"mouse.up\",\n signals: [],\n timestamp: getTimestamp4(this._recorder)\n };\n this._recorder.recordAction(mouseUp);\n }\n _optimizePath(path) {\n if (path.length < 5)\n return path;\n const optimized = this._douglasPeucker(path, 2);\n if (optimized.length < 5) {\n return this._douglasPeuckerWithMinPoints(path, 5);\n }\n return optimized;\n }\n _douglasPeucker(points, epsilon) {\n if (points.length <= 2)\n return points;\n let maxDist = 0;\n let maxIndex = 0;\n for (let i = 1; i < points.length - 1; i++) {\n const dist = this._perpendicularDistance(\n points[i],\n points[0],\n points[points.length - 1]\n );\n if (dist > maxDist) {\n maxDist = dist;\n maxIndex = i;\n }\n }\n if (maxDist > epsilon) {\n const left = this._douglasPeucker(\n points.slice(0, maxIndex + 1),\n epsilon\n );\n const right = this._douglasPeucker(\n points.slice(maxIndex),\n epsilon\n );\n return [...left.slice(0, -1), ...right];\n } else {\n return [points[0], points[points.length - 1]];\n }\n }\n _douglasPeuckerWithMinPoints(points, minPoints) {\n if (points.length <= minPoints)\n return points;\n let epsilon = 10;\n let result = this._douglasPeucker(points, epsilon);\n let high = 10;\n let low = 0;\n while (high - low > 0.1 && result.length !== minPoints) {\n epsilon = (high + low) / 2;\n result = this._douglasPeucker(points, epsilon);\n if (result.length < minPoints) {\n high = epsilon;\n } else if (result.length > minPoints) {\n low = epsilon;\n }\n }\n if (result.length < minPoints) {\n result = this._sampleEvenly(points, minPoints);\n }\n return result;\n }\n _sampleEvenly(points, count) {\n if (points.length <= count)\n return points;\n const result = [points[0]];\n const step = (points.length - 1) / (count - 1);\n for (let i = 1; i < count - 1; i++) {\n const index = Math.round(i * step);\n result.push(points[index]);\n }\n result.push(points[points.length - 1]);\n return result;\n }\n _perpendicularDistance(point, lineStart, lineEnd) {\n const dx = lineEnd.x - lineStart.x;\n const dy = lineEnd.y - lineStart.y;\n if (dx === 0 && dy === 0) {\n return Math.hypot(point.x - lineStart.x, point.y - lineStart.y);\n }\n const normalLength = Math.hypot(dx, dy);\n const distance = Math.abs(dy * point.x - dx * point.y + lineEnd.x * lineStart.y - lineEnd.y * lineStart.x) / normalLength;\n return distance;\n }\n _createOverlayCanvas() {\n const doc = this._recorder.injectedScript.document;\n this._overlayCanvas = doc.createElementNS(\"http://www.w3.org/2000/svg\", \"svg\");\n this._overlayCanvas.classList.add(\"pw-deletion-trail\");\n this._overlayCanvas.style.position = \"fixed\";\n this._overlayCanvas.style.top = \"0\";\n this._overlayCanvas.style.left = \"0\";\n this._overlayCanvas.style.width = \"100%\";\n this._overlayCanvas.style.height = \"100%\";\n this._overlayCanvas.style.pointerEvents = \"none\";\n this._overlayCanvas.style.zIndex = \"2147483646\";\n this._overlayPath = doc.createElementNS(\"http://www.w3.org/2000/svg\", \"path\");\n this._overlayPath.classList.add(\"pw-deletion-path\");\n this._overlayPath.setAttribute(\"stroke\", \"rgba(220, 53, 69, 0.5)\");\n this._overlayPath.setAttribute(\"stroke-width\", \"20\");\n this._overlayPath.setAttribute(\"stroke-linecap\", \"round\");\n this._overlayPath.setAttribute(\"stroke-linejoin\", \"round\");\n this._overlayPath.setAttribute(\"fill\", \"none\");\n this._overlayCanvas.appendChild(this._overlayPath);\n if (doc.body)\n doc.body.appendChild(this._overlayCanvas);\n }\n _removeOverlayCanvas() {\n if (this._overlayCanvas) {\n this._overlayCanvas.remove();\n this._overlayCanvas = void 0;\n this._overlayPath = void 0;\n }\n }\n _updateOverlayPath() {\n if (!this._overlayPath || this._viewportPath.length < 2)\n return;\n const d = this._viewportPath.reduce((path, point, index) => {\n const command = index === 0 ? \"M\" : \"L\";\n return `${path} ${command}${point.x},${point.y}`;\n }, \"\");\n this._overlayPath.setAttribute(\"d\", d);\n }\n _clearOverlayPath() {\n if (this._overlayPath)\n this._overlayPath.setAttribute(\"d\", \"\");\n }\n _dispatchRealMouseEvent(type, pointerEvent, target) {\n const mouseEvent = new MouseEvent(type, {\n bubbles: true,\n cancelable: true,\n view: this._recorder.injectedScript.window,\n detail: pointerEvent.detail,\n screenX: pointerEvent.screenX,\n screenY: pointerEvent.screenY,\n clientX: pointerEvent.clientX,\n clientY: pointerEvent.clientY,\n ctrlKey: pointerEvent.ctrlKey,\n altKey: pointerEvent.altKey,\n shiftKey: pointerEvent.shiftKey,\n metaKey: pointerEvent.metaKey,\n button: pointerEvent.button,\n buttons: pointerEvent.buttons,\n relatedTarget: pointerEvent.relatedTarget\n });\n target.dispatchEvent(mouseEvent);\n }\n};\n\n// packages/injected/src/recorder/skyramp/tableSnapshotTool.ts\nfunction consumeEvent2(e) {\n e.preventDefault();\n e.stopPropagation();\n e.stopImmediatePropagation();\n}\nfunction getTimestamp5(recorder) {\n return recorder.injectedScript.utils.builtins.Date.now().toString();\n}\nvar TableSnapshotTool = class {\n constructor(recorder) {\n this._hoveredTable = null;\n this._highlightModel = null;\n this._captured = false;\n this._recorder = recorder;\n }\n cursor() {\n return \"crosshair\";\n }\n install() {\n var _a;\n (_a = this._recorder.injectedScript.document.body) == null ? void 0 : _a.setAttribute(\"data-pw-cursor\", \"crosshair\");\n this._captured = false;\n }\n uninstall() {\n this._hoveredTable = null;\n this._highlightModel = null;\n this._captured = false;\n this._recorder.clearHighlight();\n }\n onKeyDown(event) {\n if (this._captured)\n return;\n if (event.key === \"Escape\") {\n consumeEvent2(event);\n this._hoveredTable = null;\n this._highlightModel = null;\n this._recorder.clearHighlight();\n this._recorder.setMode(\"recording\");\n }\n }\n onMouseMove(event) {\n if (this._captured) return;\n consumeEvent2(event);\n const target = this._findTableFromEvent(event);\n if (target !== this._hoveredTable) {\n this._hoveredTable = target;\n this._updateHighlight(target);\n }\n }\n onMouseEnter(event) {\n if (this._captured) return;\n consumeEvent2(event);\n }\n onMouseLeave(event) {\n if (this._captured) return;\n consumeEvent2(event);\n const window2 = this._recorder.injectedScript.window;\n if (window2.top !== window2 && this._recorder.deepEventTarget(event).nodeType === Node.DOCUMENT_NODE) {\n this._hoveredTable = null;\n this._highlightModel = null;\n this._recorder.clearHighlight();\n }\n }\n onClick(event) {\n if (this._captured) return;\n if (event.button !== 0) {\n consumeEvent2(event);\n return;\n }\n if (this._hoveredTable) {\n consumeEvent2(event);\n this._captureTableSnapshot(this._hoveredTable);\n }\n }\n onPointerDown(event) {\n if (this._captured) return;\n consumeEvent2(event);\n }\n onPointerUp(event) {\n if (this._captured) return;\n consumeEvent2(event);\n }\n onMouseDown(event) {\n if (this._captured) return;\n consumeEvent2(event);\n }\n onMouseUp(event) {\n if (this._captured) return;\n consumeEvent2(event);\n }\n _findTableFromEvent(event) {\n let element = this._recorder.deepEventTarget(event);\n while (element) {\n if (element.tagName === \"TABLE\") {\n return element;\n }\n element = element.parentElement;\n }\n return null;\n }\n _updateHighlight(table) {\n if (!table) {\n this._recorder.clearHighlight();\n return;\n }\n const generated = this._recorder.injectedScript.generateSelector(table, {\n testIdAttributeName: this._recorder.state.testIdAttributeName,\n multiple: false\n });\n this._highlightModel = {\n selector: generated.selector,\n elements: generated.elements,\n tooltipText: \"Click to assert table cell\",\n color: \"#4CAF5080\"\n // Green with transparency\n };\n this._recorder.updateHighlight(this._highlightModel, true);\n }\n _captureTableSnapshot(table) {\n var _a;\n const snapshot = this._extractTableData(table);\n const generated = this._recorder.injectedScript.generateSelector(table, {\n testIdAttributeName: this._recorder.state.testIdAttributeName\n });\n const action = {\n name: \"tableSnapshot\",\n selector: generated.selector,\n tableData: snapshot,\n signals: [],\n timestamp: getTimestamp5(this._recorder)\n };\n this._recorder.recordAction(action);\n this._captured = true;\n this._recorder.clearHighlight();\n this._recorder.setMode(\"recording\");\n (_a = this._recorder.overlay) == null ? void 0 : _a.flashToolSucceeded(\"recordingTableSnapshot\");\n }\n _extractTableData(table) {\n const data = {\n headers: [],\n rows: [],\n metadata: {\n rowCount: 0,\n columnCount: 0,\n hasHeaders: false,\n captureTime: (/* @__PURE__ */ new Date()).toISOString()\n }\n };\n const thead = table.querySelector(\"thead\");\n if (thead) {\n const headerRow = thead.querySelector(\"tr\");\n if (headerRow) {\n data.headers = Array.from(headerRow.querySelectorAll(\"th, td\")).map((cell) => this._getCellText(cell));\n data.metadata.hasHeaders = true;\n }\n }\n const tbody = table.querySelector(\"tbody\") || table;\n const bodyRows = tbody.querySelectorAll(\"tr\");\n data.rows = Array.from(bodyRows).map((row) => {\n return Array.from(row.querySelectorAll(\"th, td\")).map((cell) => ({\n text: this._getCellText(cell),\n isHeader: cell.tagName === \"TH\"\n }));\n });\n data.metadata.rowCount = data.rows.length;\n data.metadata.columnCount = Math.max(\n data.headers.length,\n ...data.rows.map((row) => row.length)\n );\n return data;\n }\n _getCellText(cell) {\n var _a;\n return ((_a = cell.innerText) == null ? void 0 : _a.trim()) || \"\";\n }\n};\n\n// packages/injected/src/recorder/skyramp/tableSelectorBuilder.ts\nfunction escapeTextIs(value) {\n return value.replace(/\\\\/g, \"\\\\\\\\\").replace(/\"/g, '\\\\\"');\n}\nfunction buildRowSegment(input) {\n const key = input.rowKey;\n if (key && key.value) {\n const v = escapeTextIs(key.value);\n const keyCell = `${key.tag}:nth-child(${key.colIndex + 1}):is(:text-is(\"${v}\"), :has(:text-is(\"${v}\")))`;\n return `tr:has(${keyCell})`;\n }\n return `tr:nth-child(${input.rowIndex + 1})`;\n}\nfunction buildTableCellSelector(input) {\n const rowSegment = buildRowSegment(input);\n const cellSegment = `${input.cellTag}:nth-child(${input.colIndex + 1})`;\n const core = `tbody ${rowSegment} ${cellSegment}`;\n let selector = input.tablePrefix ? `${input.tablePrefix} ${core}` : core;\n if (input.isInput)\n selector += \" input\";\n return selector;\n}\n\n// packages/injected/src/recorder/skyramp/tableAssertTool.ts\nfunction getTimestamp6(recorder) {\n return recorder.injectedScript.utils.builtins.Date.now().toString();\n}\nvar TableAssertTool = class {\n constructor(recorder) {\n this._highlightedCell = null;\n this._cellHighlight = null;\n this._assertModal = null;\n this._recorder = recorder;\n }\n cursor() {\n return \"pointer\";\n }\n install() {\n var _a;\n (_a = this._recorder.injectedScript.document.body) == null ? void 0 : _a.setAttribute(\"data-pw-cursor\", \"pointer\");\n }\n uninstall() {\n this._removeHighlight();\n this._removeModal();\n }\n cleanup() {\n this.uninstall();\n }\n onPointerMove(event) {\n const cell = this._getCellUnderPointer(event);\n if (cell && cell !== this._highlightedCell) {\n this._highlightedCell = cell;\n this._showCellHighlight(cell, true);\n } else if (!cell && this._highlightedCell) {\n this._removeHighlight();\n this._highlightedCell = null;\n }\n }\n onPointerDown(event) {\n const cell = this._getCellUnderPointer(event);\n if (!cell)\n return;\n event.preventDefault();\n event.stopPropagation();\n this._showCellHighlight(cell, false);\n this._showAssertModal(cell);\n }\n // Helper: Get table cell under pointer\n _getCellUnderPointer(event) {\n const target = event.target;\n return this._isTableCell(target) ? target : target.closest(\"td, th\");\n }\n // Helper: Check if element is a table cell\n _isTableCell(element) {\n if (!element)\n return false;\n const tagName = element.tagName.toLowerCase();\n return tagName === \"td\" || tagName === \"th\";\n }\n // Helper: Find parent table\n _findTable(cell) {\n return cell.closest(\"table\");\n }\n // Helper: Get cell position (row, col)\n _getCellPosition(cell) {\n const row = cell.closest(\"tr\");\n const table = this._findTable(cell);\n if (!row || !table)\n return { row: 0, col: 0 };\n const tbody = table.querySelector(\"tbody\");\n const rows = tbody ? Array.from(tbody.querySelectorAll(\"tr\")) : Array.from(table.querySelectorAll(\"tr\"));\n const rowIndex = rows.indexOf(row);\n const cells = Array.from(row.querySelectorAll(\"td, th\"));\n const colIndex = cells.indexOf(cell);\n return { row: rowIndex, col: colIndex };\n }\n // Helper: pick a stable identifying cell for the row (SKYR-3800).\n // Canonical row-key rule (kept identical to the NL/MCP path in\n // traceRecordingBackend._handleAssertTableCell for byte-identical JSONL):\n // the first non-empty cell whose text is not a bare integer — so a leading\n // row-number <th> \"gutter\" is skipped in favour of a real data value — and\n // only when that value is unique within the table. Returns null otherwise, so\n // the caller falls back to the ordinal row position.\n _getRowKey(table, row) {\n const cells = Array.from(row.querySelectorAll(\"td, th\"));\n const cellText = (el) => {\n var _a;\n return ((_a = el.innerText) == null ? void 0 : _a.trim()) || \"\";\n };\n const isBareInt = (s) => /^\\d+$/.test(s);\n let keyIndex = cells.findIndex((c) => cellText(c) !== \"\" && !isBareInt(cellText(c)));\n if (keyIndex === -1)\n keyIndex = cells.findIndex((c) => cellText(c) !== \"\");\n if (keyIndex === -1)\n return null;\n const keyCell = cells[keyIndex];\n const value = cellText(keyCell);\n const tbody = table.querySelector(\"tbody\");\n const bodyRows = tbody ? Array.from(tbody.querySelectorAll(\"tr\")) : Array.from(table.querySelectorAll(\"tr\"));\n const matches = bodyRows.filter((r) => {\n const c = Array.from(r.querySelectorAll(\"td, th\"))[keyIndex];\n return c && cellText(c) === value;\n });\n if (matches.length !== 1)\n return null;\n return { tag: keyCell.tagName.toLowerCase(), colIndex: keyIndex, value };\n }\n // Show cell highlight overlay\n _showCellHighlight(cell, isPreview) {\n this._removeHighlight();\n const doc = this._recorder.injectedScript.document;\n const bounds = cell.getBoundingClientRect();\n this._cellHighlight = doc.createElement(\"div\");\n this._cellHighlight.style.cssText = `\n position: fixed;\n left: ${bounds.left}px;\n top: ${bounds.top}px;\n width: ${bounds.width}px;\n height: ${bounds.height}px;\n outline: 2px ${isPreview ? \"dashed\" : \"solid\"} #4285f4;\n outline-offset: -2px;\n background-color: rgba(66, 133, 244, ${isPreview ? 0.05 : 0.15});\n pointer-events: none;\n z-index: 2147483646;\n transition: all 0.15s ease;\n `;\n if (!isPreview) {\n const checkmark = doc.createElement(\"div\");\n checkmark.textContent = \"\\u2713\";\n checkmark.style.cssText = `\n position: absolute;\n top: 2px;\n right: 2px;\n font-size: 14px;\n color: #4285f4;\n font-weight: bold;\n `;\n this._cellHighlight.appendChild(checkmark);\n }\n doc.body.appendChild(this._cellHighlight);\n }\n // Remove cell highlight\n _removeHighlight() {\n if (this._cellHighlight) {\n this._cellHighlight.remove();\n this._cellHighlight = null;\n }\n }\n // Show assertion modal\n _showAssertModal(cell) {\n var _a;\n this._removeModal();\n const doc = this._recorder.injectedScript.document;\n let cellText = \"\";\n const inputElement = cell.querySelector(\"input\");\n if (inputElement) {\n cellText = inputElement.value || \"\";\n } else {\n cellText = ((_a = cell.innerText) == null ? void 0 : _a.trim()) || \"\";\n }\n const position = this._getCellPosition(cell);\n const backdrop = doc.createElement(\"div\");\n backdrop.style.cssText = `\n position: fixed;\n top: 0;\n left: 0;\n width: 100%;\n height: 100%;\n background: rgba(0, 0, 0, 0.5);\n z-index: 2147483646;\n display: flex;\n align-items: center;\n justify-content: center;\n `;\n const modal = doc.createElement(\"div\");\n modal.style.cssText = `\n background: white;\n border-radius: 8px;\n padding: 24px;\n box-shadow: 0 8px 32px rgba(0, 0, 0, 0.3);\n max-width: 500px;\n min-width: 400px;\n font-family: system-ui, -apple-system, sans-serif;\n `;\n modal.innerHTML = `\n <div style=\"display: flex; justify-content: space-between; align-items: center; margin-bottom: 16px;\">\n <h3 style=\"margin: 0; font-size: 18px; font-weight: 600; color: #202124;\">Assert Cell Value</h3>\n <button id=\"pw-modal-close\" style=\"border: none; background: none; font-size: 24px; cursor: pointer; color: #5f6368; padding: 0; width: 24px; height: 24px; line-height: 24px;\">&times;</button>\n </div>\n <div style=\"margin-bottom: 16px;\">\n <div style=\"font-size: 13px; color: #5f6368; margin-bottom: 4px;\">Cell: Row ${position.row + 1}, Column ${position.col + 1}</div>\n <div style=\"font-size: 13px; color: #5f6368; margin-bottom: 12px;\">Current Value: \"${cellText}\"</div>\n </div>\n <div style=\"margin-bottom: 16px;\">\n <label style=\"display: block; font-size: 14px; font-weight: 500; color: #202124; margin-bottom: 8px;\">Expected Value:</label>\n <input\n id=\"pw-expected-value\"\n type=\"text\"\n value=\"${cellText.replace(/\"/g, \"&quot;\")}\"\n style=\"width: 100%; padding: 10px 12px; border: 1px solid #dadce0; border-radius: 4px; font-size: 14px; box-sizing: border-box;\"\n placeholder=\"Enter expected text...\"\n />\n </div>\n <div style=\"display: flex; justify-content: flex-end; gap: 12px; margin-top: 24px;\">\n <button id=\"pw-modal-cancel\" style=\"padding: 8px 16px; border: 1px solid #dadce0; background: white; color: #1a73e8; border-radius: 4px; cursor: pointer; font-size: 14px; font-weight: 500;\">Cancel</button>\n <button id=\"pw-modal-confirm\" style=\"padding: 8px 16px; border: none; background: #1a73e8; color: white; border-radius: 4px; cursor: pointer; font-size: 14px; font-weight: 500;\">Add Assertion</button>\n </div>\n `;\n backdrop.appendChild(modal);\n doc.body.appendChild(backdrop);\n this._assertModal = backdrop;\n const input = modal.querySelector(\"#pw-expected-value\");\n input == null ? void 0 : input.focus();\n input == null ? void 0 : input.select();\n const closeBtn = modal.querySelector(\"#pw-modal-close\");\n const cancelBtn = modal.querySelector(\"#pw-modal-cancel\");\n const confirmBtn = modal.querySelector(\"#pw-modal-confirm\");\n const onClose = () => {\n this._removeModal();\n this._removeHighlight();\n this._recorder.setMode(\"recording\");\n };\n const onConfirm = () => {\n const expectedValue = (input == null ? void 0 : input.value) || cellText;\n this._generateAssertion(cell, expectedValue);\n onClose();\n };\n closeBtn == null ? void 0 : closeBtn.addEventListener(\"click\", onClose);\n cancelBtn == null ? void 0 : cancelBtn.addEventListener(\"click\", onClose);\n confirmBtn == null ? void 0 : confirmBtn.addEventListener(\"click\", onConfirm);\n input == null ? void 0 : input.addEventListener(\"keydown\", (e) => {\n if (e.key === \"Enter\") {\n e.preventDefault();\n onConfirm();\n } else if (e.key === \"Escape\") {\n e.preventDefault();\n onClose();\n }\n });\n backdrop.addEventListener(\"click\", (e) => {\n if (e.target === backdrop)\n onClose();\n });\n }\n // Remove modal\n _removeModal() {\n if (this._assertModal) {\n this._assertModal.remove();\n this._assertModal = null;\n }\n }\n // Generate and record assertion\n _generateAssertion(cell, expectedValue) {\n var _a;\n const table = this._findTable(cell);\n if (!table) {\n console.log(\"[TableAssertTool] No table found for cell\");\n return;\n }\n const position = this._getCellPosition(cell);\n console.log(\"[TableAssertTool] Cell position:\", position);\n const inputElement = cell.querySelector(\"input\");\n const isInput = !!inputElement;\n const tableTestId = table.getAttribute(`data-${this._recorder.state.testIdAttributeName}`) || table.getAttribute(\"data-testid\");\n const tableId = table.id;\n let tablePrefix = \"\";\n if (tableTestId)\n tablePrefix = `[data-testid=\"${tableTestId}\"]`;\n else if (tableId)\n tablePrefix = `#${tableId}`;\n const row = cell.closest(\"tr\");\n const rowKey = row ? this._getRowKey(table, row) : null;\n const cellSelector = buildTableCellSelector({\n tablePrefix,\n cellTag: cell.tagName.toLowerCase(),\n colIndex: position.col,\n rowIndex: position.row,\n rowKey,\n isInput\n });\n console.log(\"[TableAssertTool] Generated selector:\", cellSelector, \"isInput:\", isInput, \"rowKey:\", rowKey == null ? void 0 : rowKey.value);\n const action = {\n name: \"assertTableCell\",\n selector: cellSelector,\n text: expectedValue,\n position,\n isInput,\n signals: [],\n timestamp: getTimestamp6(this._recorder)\n };\n console.log(\"[TableAssertTool] Recording action:\", action);\n this._recorder.recordAction(action);\n (_a = this._recorder.overlay) == null ? void 0 : _a.flashToolSucceeded(\"assertingTableCell\");\n }\n};\n\n// packages/injected/src/recorder/skyramp/visualSnapshotTool.ts\nvar HighlightColors = {\n snapshot: \"#9c7fe480\"\n // Purple for visual snapshots\n};\nfunction consumeEvent3(e) {\n e.preventDefault();\n e.stopPropagation();\n e.stopImmediatePropagation();\n}\nfunction getTimestamp7(recorder) {\n return recorder.injectedScript.utils.builtins.Date.now().toString();\n}\nfunction addEventListener3(target, eventName, listener, useCapture) {\n target.addEventListener(eventName, listener, useCapture);\n return () => target.removeEventListener(eventName, listener, useCapture);\n}\nvar VisualSnapshotTool = class _VisualSnapshotTool {\n constructor(recorder) {\n this._glassOverlay = null;\n this._marquee = null;\n this._dragStart = null;\n this._dragCurrent = null;\n this._isDragging = false;\n this._hoverHighlight = null;\n this._listeners = [];\n this._syntheticHighlightEl = null;\n // Constants\n this.DOUBLE_TOGGLE_TIMEOUT = 1500;\n // 1.5 seconds\n this.DRAG_THRESHOLD = 8;\n // pixels\n this.VIEWPORT_THRESHOLD = 0.8;\n this._recorder = recorder;\n }\n // 80% of viewport\n static async getNextCounter(recorder, type) {\n try {\n if (typeof recorder.injectedScript.window.__pw_recorderIncrementCounter === \"function\") {\n return await recorder.injectedScript.window.__pw_recorderIncrementCounter(type);\n }\n } catch (e) {\n console.error(\"Failed to get counter from server:\", e);\n }\n return Date.now() % 1e3;\n }\n cursor() {\n return this._isDragging ? \"crosshair\" : \"pointer\";\n }\n install() {\n var _a;\n this._createGlassOverlay();\n (_a = this._recorder.injectedScript.document.body) == null ? void 0 : _a.setAttribute(\"data-pw-cursor\", \"pointer\");\n }\n uninstall() {\n var _a;\n this._removeGlassOverlay();\n this._removeMarquee();\n this._cleanup();\n (_a = this._recorder.injectedScript.document.body) == null ? void 0 : _a.removeAttribute(\"data-pw-cursor\");\n }\n cleanup() {\n this._cleanup();\n }\n _cleanup() {\n this._listeners.forEach((remove) => remove());\n this._listeners = [];\n if (this._hoverHighlight && this._recorder) {\n this._recorder.updateHighlight(null, false);\n this._hoverHighlight = null;\n }\n this._cleanupSyntheticHighlight();\n this._dragStart = null;\n this._dragCurrent = null;\n this._isDragging = false;\n }\n _cleanupSyntheticHighlight() {\n if (this._syntheticHighlightEl) {\n this._syntheticHighlightEl.remove();\n this._syntheticHighlightEl = null;\n }\n }\n onKeyDown(event) {\n if (event.key === \"Escape\") {\n consumeEvent3(event);\n this._cancelSnapshot();\n }\n }\n _createGlassOverlay() {\n const doc = this._recorder.injectedScript.document;\n this._glassOverlay = doc.createElement(\"x-pw-glass\");\n this._glassOverlay.style.cssText = `\n position: fixed !important;\n top: 0 !important;\n left: 0 !important;\n right: 0 !important;\n bottom: 0 !important;\n z-index: 2147483646 !important;\n background: rgba(0, 120, 215, 0.05) !important;\n cursor: pointer !important;\n pointer-events: auto !important;\n `;\n this._listeners.push(addEventListener3(this._glassOverlay, \"pointerdown\", (e) => this._onGlassPointerDown(e), true));\n this._listeners.push(addEventListener3(this._glassOverlay, \"pointermove\", (e) => this._onGlassPointerMove(e), true));\n this._listeners.push(addEventListener3(this._glassOverlay, \"pointerup\", (e) => this._onGlassPointerUp(e), true));\n this._listeners.push(addEventListener3(this._glassOverlay, \"click\", (e) => consumeEvent3(e), true));\n if (doc.body)\n doc.body.appendChild(this._glassOverlay);\n }\n _removeGlassOverlay() {\n if (this._glassOverlay) {\n this._glassOverlay.remove();\n this._glassOverlay = null;\n }\n }\n _createMarquee() {\n if (this._marquee)\n return;\n const doc = this._recorder.injectedScript.document;\n this._marquee = doc.createElement(\"x-pw-marquee\");\n this._marquee.style.cssText = `\n position: fixed !important;\n border: 2px dashed #0078d7 !important;\n background: rgba(0, 120, 215, 0.1) !important;\n z-index: 2147483647 !important;\n pointer-events: none !important;\n `;\n doc.body.appendChild(this._marquee);\n }\n _updateMarquee() {\n if (!this._marquee || !this._dragStart || !this._dragCurrent)\n return;\n const x1 = Math.min(this._dragStart.x, this._dragCurrent.x);\n const y1 = Math.min(this._dragStart.y, this._dragCurrent.y);\n const x2 = Math.max(this._dragStart.x, this._dragCurrent.x);\n const y2 = Math.max(this._dragStart.y, this._dragCurrent.y);\n this._marquee.style.left = x1 + \"px\";\n this._marquee.style.top = y1 + \"px\";\n this._marquee.style.width = x2 - x1 + \"px\";\n this._marquee.style.height = y2 - y1 + \"px\";\n }\n _removeMarquee() {\n if (this._marquee) {\n this._marquee.remove();\n this._marquee = null;\n }\n }\n _onGlassPointerDown(event) {\n consumeEvent3(event);\n this._dragStart = { x: event.clientX, y: event.clientY };\n this._dragCurrent = this._dragStart;\n }\n _onGlassPointerMove(event) {\n consumeEvent3(event);\n if (!this._dragStart) {\n this._updateHoverHighlight(event);\n return;\n }\n this._dragCurrent = { x: event.clientX, y: event.clientY };\n const distance = Math.hypot(\n this._dragCurrent.x - this._dragStart.x,\n this._dragCurrent.y - this._dragStart.y\n );\n if (distance >= this.DRAG_THRESHOLD && !this._isDragging) {\n this._isDragging = true;\n this._createMarquee();\n if (this._glassOverlay)\n this._glassOverlay.style.cursor = \"crosshair\";\n }\n if (this._isDragging) {\n this._updateMarquee();\n } else {\n this._updateHoverHighlight(event);\n }\n }\n async _onGlassPointerUp(event) {\n consumeEvent3(event);\n if (this._isDragging) {\n await this._captureRegionSnapshot();\n } else if (this._dragStart) {\n await this._captureClickSnapshot(event);\n }\n this._recorder.setMode(\"recording\");\n }\n _updateHoverHighlight(event) {\n var _a, _b, _c, _d;\n if (!this._recorder)\n return;\n if (this._glassOverlay)\n this._glassOverlay.style.display = \"none\";\n const rawTarget = this._recorder.document.elementFromPoint(event.clientX, event.clientY);\n if (this._glassOverlay)\n this._glassOverlay.style.display = \"\";\n if (!rawTarget)\n return;\n if (((_a = rawTarget.tagName) == null ? void 0 : _a.toLowerCase()) === \"iframe\") {\n const iframe = rawTarget;\n try {\n const iframeRect = iframe.getBoundingClientRect();\n const iframeDoc = iframe.contentDocument;\n if (iframeDoc) {\n const initialChain = [{ iframe, selector: this._generateStableSelector(iframe) }];\n const localX = event.clientX - iframeRect.left;\n const localY = event.clientY - iframeRect.top;\n const gojs = this._findGoJSDiagramRecursive(\n initialChain,\n iframeDoc,\n localX,\n localY,\n iframeRect.left,\n iframeRect.top\n );\n if (gojs) {\n const containerRect = gojs.containerEl.getBoundingClientRect();\n const mainLeft = gojs.accOffsetX + containerRect.left;\n const mainTop = gojs.accOffsetY + containerRect.top;\n if (this._syntheticHighlightEl) {\n const s = this._syntheticHighlightEl.style;\n if (s.left === `${mainLeft}px` && s.top === `${mainTop}px`)\n return;\n }\n this._cleanupSyntheticHighlight();\n const doc = this._recorder.document;\n const synth = doc.createElement(\"x-pw-gojs-highlight\");\n synth.style.cssText = [\n \"position: fixed\",\n \"pointer-events: none\",\n \"z-index: -1\",\n `left: ${mainLeft}px`,\n `top: ${mainTop}px`,\n `width: ${containerRect.width}px`,\n `height: ${containerRect.height}px`\n ].join(\" !important; \") + \" !important;\";\n (_b = doc.body) == null ? void 0 : _b.appendChild(synth);\n this._syntheticHighlightEl = synth;\n const generated2 = this._recorder.injectedScript.generateSelector(iframe, {\n testIdAttributeName: this._recorder.state.testIdAttributeName\n });\n this._hoverHighlight = {\n selector: generated2.selector,\n elements: [synth],\n color: HighlightColors.snapshot,\n tooltipText: \"GoJS diagram (iframe)\"\n };\n this._recorder.updateHighlight(this._hoverHighlight, true);\n return;\n }\n const found = this._findElementInIframeRecursive(\n initialChain,\n iframeDoc,\n localX,\n localY,\n iframeRect.left,\n iframeRect.top\n );\n if (found) {\n const elRect = found.element.getBoundingClientRect();\n const mainLeft = found.accOffsetX + elRect.left;\n const mainTop = found.accOffsetY + elRect.top;\n if (this._syntheticHighlightEl) {\n const s = this._syntheticHighlightEl.style;\n if (s.left === `${mainLeft}px` && s.top === `${mainTop}px`)\n return;\n }\n this._cleanupSyntheticHighlight();\n const doc = this._recorder.document;\n const synth = doc.createElement(\"x-pw-gojs-highlight\");\n synth.style.cssText = [\n \"position: fixed\",\n \"pointer-events: none\",\n \"z-index: -1\",\n `left: ${mainLeft}px`,\n `top: ${mainTop}px`,\n `width: ${elRect.width}px`,\n `height: ${elRect.height}px`\n ].join(\" !important; \") + \" !important;\";\n (_c = doc.body) == null ? void 0 : _c.appendChild(synth);\n this._syntheticHighlightEl = synth;\n const selector = this._generateStableSelector(found.element);\n this._hoverHighlight = {\n selector,\n elements: [synth],\n color: HighlightColors.snapshot,\n tooltipText: \"Element (iframe)\"\n };\n this._recorder.updateHighlight(this._hoverHighlight, true);\n return;\n }\n }\n } catch (e) {\n }\n this._cleanupSyntheticHighlight();\n } else {\n this._cleanupSyntheticHighlight();\n }\n const target = this._resolvePdfTarget(rawTarget) || rawTarget;\n if (((_d = this._hoverHighlight) == null ? void 0 : _d.elements[0]) === target)\n return;\n const generated = this._recorder.injectedScript.generateSelector(target, {\n testIdAttributeName: this._recorder.state.testIdAttributeName\n });\n this._hoverHighlight = {\n selector: generated.selector,\n elements: generated.elements,\n color: HighlightColors.snapshot\n };\n this._recorder.updateHighlight(this._hoverHighlight, true);\n }\n async _captureClickSnapshot(event) {\n var _a;\n const glassWasVisible = this._glassOverlay && this._glassOverlay.style.display !== \"none\";\n if (this._glassOverlay)\n this._glassOverlay.style.display = \"none\";\n const rawTarget = this._recorder.document.elementFromPoint(event.clientX, event.clientY);\n if (this._glassOverlay && glassWasVisible)\n this._glassOverlay.style.display = \"\";\n if (!rawTarget)\n return;\n if (((_a = rawTarget.tagName) == null ? void 0 : _a.toLowerCase()) === \"iframe\") {\n const iframe = rawTarget;\n try {\n const iframeRect = iframe.getBoundingClientRect();\n const iframeDoc = iframe.contentDocument;\n if (iframeDoc) {\n const initialChain = [{ iframe, selector: this._generateStableSelector(iframe) }];\n const localX = event.clientX - iframeRect.left;\n const localY = event.clientY - iframeRect.top;\n const gojs = this._findGoJSDiagramRecursive(\n initialChain,\n iframeDoc,\n localX,\n localY,\n iframeRect.left,\n iframeRect.top\n );\n if (gojs) {\n await this._captureGoJsDiagramSnapshot(gojs.iframeChain, gojs.diagramSelector);\n return;\n }\n const found = this._findElementInIframeRecursive(\n initialChain,\n iframeDoc,\n localX,\n localY,\n iframeRect.left,\n iframeRect.top\n );\n if (found) {\n await this._captureIframeElementSnapshot(found.iframeChain, found.element);\n return;\n }\n }\n } catch (e) {\n }\n await this._captureElementSnapshot(iframe);\n return;\n }\n const forcePageSnapshot = event.altKey;\n const snapParent = event.shiftKey && rawTarget.parentElement;\n const baseTarget = snapParent ? rawTarget.parentElement : rawTarget;\n const pdfPageWrapper = this._resolvePdfTarget(baseTarget);\n const actualTarget = pdfPageWrapper || baseTarget;\n const isPageSnapshot = forcePageSnapshot || !pdfPageWrapper && this._shouldCapturePageSnapshot(actualTarget);\n if (isPageSnapshot) {\n await this._capturePageSnapshot();\n } else {\n await this._captureElementSnapshot(actualTarget);\n }\n }\n /**\n * Resolves a PDF text layer element or its descendant to the containing\n * canvasWrapper (the div with [data-page-number]). Returns null if the\n * element is not inside a PDF page.\n */\n _resolvePdfTarget(element) {\n const wrapper = element.closest(\"[data-page-number]\");\n if (wrapper)\n return wrapper;\n return null;\n }\n _shouldCapturePageSnapshot(element, contextWindow) {\n var _a;\n const tagName = (_a = element.tagName) == null ? void 0 : _a.toLowerCase();\n if (tagName === \"html\" || tagName === \"body\")\n return true;\n const win = contextWindow != null ? contextWindow : this._recorder.injectedScript.window;\n const rect = element.getBoundingClientRect();\n const viewportArea = win.innerWidth * win.innerHeight;\n const elementArea = rect.width * rect.height;\n return elementArea >= viewportArea * this.VIEWPORT_THRESHOLD;\n }\n /**\n * Builds a stable CSS selector for an element using id, data-testid, or\n * nth-child position as fallback — mirrors the logic in dragDropTool._findGoJSContainer.\n */\n _buildSelectorFromEl(el) {\n if (el.id)\n return `#${el.id}`;\n const testId = el.getAttribute(\"data-testid\");\n if (testId)\n return `[data-testid=\"${testId}\"]`;\n const parent = el.parentElement;\n if (parent) {\n const idx = Array.from(parent.children).indexOf(el) + 1;\n return `${el.tagName.toLowerCase()}:nth-child(${idx})`;\n }\n return el.tagName.toLowerCase();\n }\n /**\n * Recursively pierces through nested iframes from a click/hover position,\n * walking up the DOM at each level to find a GoJS diagram container.\n *\n * @param iframeChain Accumulated iframe selector chain (outermost first).\n * @param currentDoc The document to search in at this recursion level.\n * @param localX/localY Click position in currentDoc's own viewport coords.\n * @param accOffsetX/Y Accumulated offset to add to element rects for main-doc coords.\n */\n _findGoJSDiagramRecursive(iframeChain, currentDoc, localX, localY, accOffsetX, accOffsetY) {\n var _a, _b, _c;\n const glassInDoc = currentDoc.querySelector(\"x-pw-glass\");\n if (glassInDoc)\n glassInDoc.style.display = \"none\";\n let innerTarget = null;\n try {\n innerTarget = currentDoc.elementFromPoint(localX, localY);\n } catch (e) {\n if (glassInDoc)\n glassInDoc.style.display = \"\";\n return null;\n }\n if (glassInDoc)\n glassInDoc.style.display = \"\";\n if (!innerTarget)\n return null;\n if (((_a = innerTarget.tagName) == null ? void 0 : _a.toLowerCase()) === \"iframe\") {\n const nested = innerTarget;\n try {\n const nestedDoc = nested.contentDocument;\n if (!nestedDoc)\n return null;\n const nestedRect = nested.getBoundingClientRect();\n return this._findGoJSDiagramRecursive(\n [...iframeChain, { iframe: nested, selector: this._generateStableSelector(nested) }],\n nestedDoc,\n localX - nestedRect.left,\n localY - nestedRect.top,\n accOffsetX + nestedRect.left,\n accOffsetY + nestedRect.top\n );\n } catch (e) {\n return null;\n }\n }\n const win = currentDoc.defaultView;\n if (!((_c = (_b = win == null ? void 0 : win.go) == null ? void 0 : _b.Diagram) == null ? void 0 : _c.fromDiv))\n return null;\n const body = currentDoc.body;\n let el = innerTarget;\n while (el && el !== body) {\n if (win.go.Diagram.fromDiv(el))\n return { containerEl: el, diagramSelector: this._buildSelectorFromEl(el), iframeChain, accOffsetX, accOffsetY };\n el = el.parentElement;\n }\n return null;\n }\n /**\n * Recursively pierces through nested iframes to find the actual element\n * at the given viewport position. Returns the element, its iframe chain,\n * and the iframe document — or null if cross-origin or inaccessible.\n */\n _findElementInIframeRecursive(iframeChain, currentDoc, localX, localY, accOffsetX, accOffsetY) {\n var _a;\n const glass = currentDoc.querySelector(\"x-pw-glass\");\n if (glass)\n glass.style.display = \"none\";\n let innerTarget = null;\n try {\n innerTarget = currentDoc.elementFromPoint(localX, localY);\n } catch (e) {\n if (glass)\n glass.style.display = \"\";\n return null;\n }\n if (glass)\n glass.style.display = \"\";\n if (!innerTarget)\n return null;\n if (((_a = innerTarget.tagName) == null ? void 0 : _a.toLowerCase()) === \"iframe\") {\n const nested = innerTarget;\n try {\n const nestedDoc = nested.contentDocument;\n if (!nestedDoc)\n return null;\n const nestedRect = nested.getBoundingClientRect();\n return this._findElementInIframeRecursive(\n [...iframeChain, { iframe: nested, selector: this._generateStableSelector(nested) }],\n nestedDoc,\n localX - nestedRect.left,\n localY - nestedRect.top,\n accOffsetX + nestedRect.left,\n accOffsetY + nestedRect.top\n );\n } catch (e) {\n return null;\n }\n }\n return { element: innerTarget, iframeChain, document: currentDoc, accOffsetX, accOffsetY };\n }\n /**\n * Generates a stable selector for an element by delegating to the polling\n * recorder of the element's own document (__pw_recorderGenerateSelector\n * exposed by pollingRecorder.ts). This routes through Playwright's full\n * selector machinery (incl. ScopingHandler dynamic-ID filtering), so an\n * iframe with id=\"_commonPopup677_iframe\" yields a stable form like\n * iframe[src*=\"...\"] / iframe[title=\"...\"] / iframe[name=\"...\"] instead\n * of the random ID. Used for both iframe elements (when seeding the\n * iframe chain) and elements found inside an iframe. Falls back to\n * _buildSelectorFromEl when the polling recorder is unavailable\n * (e.g. cross-origin or recorder not yet attached).\n */\n _generateStableSelector(element) {\n var _a;\n try {\n const win = (_a = element.ownerDocument) == null ? void 0 : _a.defaultView;\n if (win == null ? void 0 : win.__pw_recorderGenerateSelector) {\n const generated = win.__pw_recorderGenerateSelector(element, {\n testIdAttributeName: this._recorder.state.testIdAttributeName\n });\n if (generated == null ? void 0 : generated.selector)\n return generated.selector;\n }\n } catch (e) {\n }\n return this._buildSelectorFromEl(element);\n }\n /**\n * Records an element visualSnapshot action for an element found inside\n * one or more iframes, including the iframe selector chain.\n */\n async _captureIframeElementSnapshot(iframeChain, element) {\n var _a;\n const selector = this._generateStableSelector(element);\n const counter = await _VisualSnapshotTool.getNextCounter(this._recorder, \"element\");\n const filename = `el-${String(counter).padStart(3, \"0\")}.png`;\n const action = {\n name: \"visualSnapshot\",\n snapshotType: \"element\",\n iframeSelectors: iframeChain.map((item) => item.selector),\n selector,\n filename,\n signals: [],\n timestamp: getTimestamp7(this._recorder)\n };\n this._recorder.recordAction(action);\n (_a = this._recorder.overlay) == null ? void 0 : _a.flashToolSucceeded(\"assertingVSnapshot\");\n }\n /**\n * Records a gojsDiagram visualSnapshot action for a GoJS canvas found inside\n * one or more nested iframes.\n */\n async _captureGoJsDiagramSnapshot(iframeChain, diagramSelector) {\n var _a;\n const counter = await _VisualSnapshotTool.getNextCounter(this._recorder, \"element\");\n const filename = `gojs-${String(counter).padStart(3, \"0\")}.png`;\n const action = {\n name: \"visualSnapshot\",\n snapshotType: \"gojsDiagram\",\n iframeSelectors: iframeChain.map((item) => item.selector),\n diagramSelector,\n filename,\n signals: [],\n timestamp: getTimestamp7(this._recorder)\n };\n this._recorder.recordAction(action);\n (_a = this._recorder.overlay) == null ? void 0 : _a.flashToolSucceeded(\"assertingVSnapshot\");\n }\n async _capturePageSnapshot() {\n var _a;\n const counter = await _VisualSnapshotTool.getNextCounter(this._recorder, \"page\");\n const filename = `page-${String(counter).padStart(3, \"0\")}.png`;\n const action = {\n name: \"visualSnapshot\",\n snapshotType: \"page\",\n filename,\n fullPage: true,\n signals: [],\n timestamp: getTimestamp7(this._recorder)\n };\n this._recorder.recordAction(action);\n (_a = this._recorder.overlay) == null ? void 0 : _a.flashToolSucceeded(\"assertingVSnapshot\");\n }\n async _captureElementSnapshot(element) {\n var _a;\n const generated = this._recorder.injectedScript.generateSelector(element, {\n testIdAttributeName: this._recorder.state.testIdAttributeName\n });\n const counter = await _VisualSnapshotTool.getNextCounter(this._recorder, \"element\");\n const filename = `el-${String(counter).padStart(3, \"0\")}.png`;\n const action = {\n name: \"visualSnapshot\",\n snapshotType: \"element\",\n selector: generated.selector,\n filename,\n signals: [],\n timestamp: getTimestamp7(this._recorder)\n };\n this._recorder.recordAction(action);\n (_a = this._recorder.overlay) == null ? void 0 : _a.flashToolSucceeded(\"assertingVSnapshot\");\n }\n async _captureRegionSnapshot() {\n var _a;\n if (!this._dragStart || !this._dragCurrent)\n return;\n const x1 = Math.min(this._dragStart.x, this._dragCurrent.x);\n const y1 = Math.min(this._dragStart.y, this._dragCurrent.y);\n const x2 = Math.max(this._dragStart.x, this._dragCurrent.x);\n const y2 = Math.max(this._dragStart.y, this._dragCurrent.y);\n const scrollX = this._recorder.injectedScript.window.scrollX || this._recorder.injectedScript.window.pageXOffset;\n const scrollY = this._recorder.injectedScript.window.scrollY || this._recorder.injectedScript.window.pageYOffset;\n const clip = {\n x: Math.round(x1 + scrollX),\n y: Math.round(y1 + scrollY),\n width: Math.round(x2 - x1),\n height: Math.round(y2 - y1)\n };\n const counter = await _VisualSnapshotTool.getNextCounter(this._recorder, \"region\");\n const filename = `region-${String(counter).padStart(3, \"0\")}.png`;\n const action = {\n name: \"visualSnapshot\",\n snapshotType: \"region\",\n clip,\n filename,\n signals: [],\n timestamp: getTimestamp7(this._recorder)\n };\n this._recorder.recordAction(action);\n (_a = this._recorder.overlay) == null ? void 0 : _a.flashToolSucceeded(\"assertingVSnapshot\");\n }\n _cancelSnapshot() {\n this._recorder.setMode(\"recording\");\n }\n};\n\n// packages/injected/src/recorder/skyramp/areaSelectionTool.ts\nfunction consumeEvent4(e) {\n e.preventDefault();\n e.stopPropagation();\n e.stopImmediatePropagation();\n}\nfunction getTimestamp8(recorder) {\n return recorder.injectedScript.utils.builtins.Date.now().toString();\n}\nfunction addEventListener4(target, eventName, listener, useCapture) {\n target.addEventListener(eventName, listener, useCapture);\n return () => target.removeEventListener(eventName, listener, useCapture);\n}\nfunction removeEventListeners2(listeners) {\n for (const listener of listeners)\n listener();\n listeners.splice(0, listeners.length);\n}\nvar AreaSelectionTool = class {\n constructor(recorder) {\n this._selectionState = null;\n this._overlay = null;\n this._feedbackTooltip = null;\n this._listeners = [];\n this._recorder = recorder;\n this._initializeConfig();\n }\n _initializeConfig() {\n const win = this._recorder.injectedScript.window;\n if (!win.__playwrightAreaSelectionConfig) {\n win.__playwrightAreaSelectionConfig = {\n minDragDistance: 3,\n // Lowered from 5 to 3 pixels for better sensitivity\n showFeedback: true,\n feedbackDuration: 2e3,\n // 2 seconds\n overlayColor: \"#0ea5e9\",\n // Sky blue\n overlayOpacity: 0.1,\n overlayBorderStyle: \"dashed\",\n overlayBorderWidth: 2\n };\n console.log(\"[AreaSelectionTool] Configuration available at window.__playwrightAreaSelectionConfig\");\n console.log(\"[AreaSelectionTool] Adjust minDragDistance (default: 3px) in DevTools to fine-tune sensitivity\");\n }\n }\n _getConfig() {\n return this._recorder.injectedScript.window.__playwrightAreaSelectionConfig;\n }\n cursor() {\n return \"crosshair\";\n }\n install() {\n this._arm();\n }\n uninstall() {\n this._disarm();\n this._removeOverlay();\n this._removeFeedbackTooltip();\n }\n cleanup() {\n if (this._selectionState) {\n this._disarm();\n this._removeOverlay();\n this._removeFeedbackTooltip();\n }\n }\n onKeyDown(event) {\n if (event.key === \"Escape\") {\n consumeEvent4(event);\n this._removeOverlay();\n this._recorder.setMode(\"recording\");\n }\n }\n _arm() {\n var _a;\n this._selectionState = {\n startPoint: null,\n endPoint: null,\n targetCanvas: null,\n isSelecting: false\n };\n (_a = this._recorder.injectedScript.document.body) == null ? void 0 : _a.setAttribute(\"data-pw-cursor\", \"crosshair\");\n this._createOverlay();\n const onPointerDown = (e) => {\n var _a2;\n const pointerEvent = e;\n if (!this._selectionState)\n return;\n const target = pointerEvent.target;\n if (this._isInteractiveElement(target)) {\n console.log(\"[AreaSelectionTool] Ignoring click on interactive element:\", target.tagName, (_a2 = target.textContent) == null ? void 0 : _a2.substring(0, 30));\n return;\n }\n this._selectionState.startPoint = { x: pointerEvent.clientX, y: pointerEvent.clientY };\n const canvas = this._detectCanvasAtPoint({ x: pointerEvent.clientX, y: pointerEvent.clientY });\n if (canvas) {\n this._selectionState.targetCanvas = canvas;\n } else {\n this._selectionState.targetCanvas = null;\n }\n this._selectionState.isSelecting = true;\n };\n const onPointerMove = (e) => {\n const pointerEvent = e;\n if (this._selectionState && this._selectionState.isSelecting && this._selectionState.startPoint) {\n this._selectionState.endPoint = { x: pointerEvent.clientX, y: pointerEvent.clientY };\n this._updateOverlay(this._selectionState.startPoint, this._selectionState.endPoint);\n }\n };\n const onPointerUp = (e) => {\n const pointerEvent = e;\n if (this._selectionState && this._selectionState.isSelecting) {\n this._selectionState.endPoint = { x: pointerEvent.clientX, y: pointerEvent.clientY };\n this._selectionState.isSelecting = false;\n if (this._selectionState.startPoint && this._selectionState.endPoint) {\n const dragDistance = this._calculateDistance(\n this._selectionState.startPoint,\n this._selectionState.endPoint\n );\n const config = this._getConfig();\n const MIN_DRAG_DISTANCE = config.minDragDistance;\n if (dragDistance >= MIN_DRAG_DISTANCE) {\n console.log(\"[AreaSelectionTool] \\u2713 Capturing area selection, drag distance:\", dragDistance.toFixed(1), \"px (threshold:\", MIN_DRAG_DISTANCE, \"px)\");\n this._showSuccessFeedback(dragDistance);\n this._capture();\n this._selectionState.startPoint = null;\n this._selectionState.endPoint = null;\n this._selectionState.targetCanvas = null;\n this._removeOverlay();\n } else {\n console.warn(\"[AreaSelectionTool] \\u2717 Drag too small:\", dragDistance.toFixed(1), \"px (need \\u2265\", MIN_DRAG_DISTANCE, \"px) - staying active for retry\");\n this._showFailureFeedback(dragDistance, MIN_DRAG_DISTANCE);\n this._selectionState.startPoint = null;\n this._selectionState.endPoint = null;\n this._selectionState.targetCanvas = null;\n this._removeOverlay();\n }\n }\n }\n };\n this._listeners.push(\n addEventListener4(this._recorder.document, \"pointerdown\", onPointerDown, false),\n addEventListener4(this._recorder.document, \"pointermove\", onPointerMove, false),\n addEventListener4(this._recorder.document, \"pointerup\", onPointerUp, false)\n );\n }\n _disarm() {\n removeEventListeners2(this._listeners);\n this._listeners = [];\n this._selectionState = null;\n }\n _createOverlay() {\n const config = this._getConfig();\n this._overlay = this._recorder.document.createElement(\"div\");\n const hexToRgba = (hex, opacity) => {\n const r = parseInt(hex.slice(1, 3), 16);\n const g = parseInt(hex.slice(3, 5), 16);\n const b = parseInt(hex.slice(5, 7), 16);\n return `rgba(${r}, ${g}, ${b}, ${opacity})`;\n };\n this._overlay.style.cssText = `\n position: fixed;\n border: ${config.overlayBorderWidth}px ${config.overlayBorderStyle} ${config.overlayColor};\n background: ${hexToRgba(config.overlayColor, config.overlayOpacity)};\n pointer-events: none;\n z-index: 2147483646;\n display: none;\n box-shadow: 0 0 0 1px rgba(0, 0, 0, 0.1);\n transition: opacity 0.15s ease-in-out;\n `;\n this._recorder.document.body.appendChild(this._overlay);\n }\n _updateOverlay(start, end) {\n if (!this._overlay)\n return;\n const left = Math.min(start.x, end.x);\n const top = Math.min(start.y, end.y);\n const width = Math.abs(end.x - start.x);\n const height = Math.abs(end.y - start.y);\n this._overlay.style.left = `${left}px`;\n this._overlay.style.top = `${top}px`;\n this._overlay.style.width = `${width}px`;\n this._overlay.style.height = `${height}px`;\n this._overlay.style.display = \"block\";\n }\n _removeOverlay() {\n if (this._overlay && this._overlay.parentElement) {\n this._overlay.parentElement.removeChild(this._overlay);\n this._overlay = null;\n }\n }\n _detectCanvasAtPoint(point) {\n const element = this._recorder.document.elementFromPoint(point.x, point.y);\n if ((element == null ? void 0 : element.tagName) === \"CANVAS\") {\n return element;\n }\n return null;\n }\n _detectCanvasContext(point) {\n const element = this._recorder.document.elementFromPoint(point.x, point.y);\n if ((element == null ? void 0 : element.tagName) === \"CANVAS\") {\n const canvas = element;\n const rect = canvas.getBoundingClientRect();\n return { canvas, rect };\n }\n return null;\n }\n _isInteractiveElement(element) {\n var _a, _b;\n if (!element)\n return false;\n let current = element;\n while (current && current !== this._recorder.document.body) {\n const tagName = (_a = current.tagName) == null ? void 0 : _a.toLowerCase();\n if ([\"button\", \"a\", \"input\", \"select\", \"textarea\", \"label\"].includes(tagName))\n return true;\n const role = current.getAttribute(\"role\");\n if (role && [\"button\", \"link\", \"menuitem\", \"tab\", \"checkbox\", \"radio\", \"switch\", \"textbox\"].includes(role))\n return true;\n if (current.hasAttribute(\"onclick\") || current.getAttribute(\"data-testid\"))\n return true;\n const className = ((_b = current.className) == null ? void 0 : _b.toString()) || \"\";\n if (className.match(/btn|button|link|clickable|action/i))\n return true;\n current = current.parentElement;\n }\n return false;\n }\n _calculateDistance(start, end) {\n const dx = end.x - start.x;\n const dy = end.y - start.y;\n return Math.sqrt(dx * dx + dy * dy);\n }\n _showSuccessFeedback(distance) {\n const config = this._getConfig();\n if (!config.showFeedback)\n return;\n this._showFeedbackTooltip(\n `\\u2713 Selection captured (${distance.toFixed(1)}px)`,\n \"#10b981\",\n // Green\n config.feedbackDuration\n );\n }\n _showFailureFeedback(distance, threshold) {\n const config = this._getConfig();\n if (!config.showFeedback)\n return;\n this._showFeedbackTooltip(\n `\\u2717 Drag too small: ${distance.toFixed(1)}px (need \\u2265${threshold}px)`,\n \"#ef4444\",\n // Red\n config.feedbackDuration\n );\n }\n _showFeedbackTooltip(message, color, duration) {\n this._removeFeedbackTooltip();\n this._feedbackTooltip = this._recorder.document.createElement(\"div\");\n this._feedbackTooltip.textContent = message;\n this._feedbackTooltip.style.cssText = `\n position: fixed;\n top: 20px;\n left: 50%;\n transform: translateX(-50%);\n background: ${color};\n color: white;\n padding: 12px 24px;\n border-radius: 6px;\n font-family: system-ui, -apple-system, sans-serif;\n font-size: 14px;\n font-weight: 500;\n box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15);\n z-index: 2147483647;\n pointer-events: none;\n animation: pwSlideIn 0.3s ease-out;\n `;\n const style = this._recorder.document.createElement(\"style\");\n style.textContent = `\n @keyframes pwSlideIn {\n from {\n opacity: 0;\n transform: translateX(-50%) translateY(-10px);\n }\n to {\n opacity: 1;\n transform: translateX(-50%) translateY(0);\n }\n }\n @keyframes pwSlideOut {\n from {\n opacity: 1;\n transform: translateX(-50%) translateY(0);\n }\n to {\n opacity: 0;\n transform: translateX(-50%) translateY(-10px);\n }\n }\n `;\n this._recorder.document.head.appendChild(style);\n this._recorder.document.body.appendChild(this._feedbackTooltip);\n setTimeout(() => {\n if (this._feedbackTooltip) {\n this._feedbackTooltip.style.animation = \"pwSlideOut 0.3s ease-in\";\n setTimeout(() => this._removeFeedbackTooltip(), 300);\n }\n }, duration);\n }\n _removeFeedbackTooltip() {\n if (this._feedbackTooltip && this._feedbackTooltip.parentElement) {\n this._feedbackTooltip.parentElement.removeChild(this._feedbackTooltip);\n this._feedbackTooltip = null;\n }\n }\n _getElementsInRect(rect) {\n const elements = [];\n const candidates = this._recorder.document.querySelectorAll(\"*\");\n for (const el of candidates) {\n const bounds = el.getBoundingClientRect();\n if (!(bounds.right < rect.left || bounds.left > rect.right || bounds.bottom < rect.top || bounds.top > rect.bottom)) {\n const tagName = el.tagName.toLowerCase();\n const isInteractive = [\"button\", \"a\", \"input\", \"select\", \"textarea\"].includes(tagName) || el.hasAttribute(\"role\") || el.hasAttribute(\"data-testid\");\n if (isInteractive)\n elements.push(el);\n }\n }\n return elements;\n }\n _capture() {\n var _a, _b;\n const start = this._selectionState.startPoint;\n const end = this._selectionState.endPoint;\n const left = Math.min(start.x, end.x);\n const top = Math.min(start.y, end.y);\n const width = Math.abs(end.x - start.x);\n const height = Math.abs(end.y - start.y);\n const rect = new DOMRect(left, top, width, height);\n let action;\n const canvas = this._selectionState.targetCanvas || ((_a = this._detectCanvasContext({ x: left + width / 2, y: top + height / 2 })) == null ? void 0 : _a.canvas);\n if (canvas) {\n const canvasGenerated = this._recorder.injectedScript.generateSelector(canvas, {\n testIdAttributeName: this._recorder.state.testIdAttributeName\n });\n action = {\n name: \"selectArea\",\n type: \"canvas\",\n startPoint: start,\n endPoint: end,\n canvasSelector: canvasGenerated.selector,\n signals: [],\n timestamp: getTimestamp8(this._recorder)\n };\n } else {\n const elements = this._getElementsInRect(rect);\n const selectors = elements.map((el) => {\n const generated = this._recorder.injectedScript.generateSelector(el, {\n testIdAttributeName: this._recorder.state.testIdAttributeName\n });\n return generated.selector;\n });\n action = {\n name: \"selectArea\",\n type: elements.length > 0 ? \"dom\" : \"hybrid\",\n startPoint: start,\n endPoint: end,\n selectors: selectors.length > 0 ? selectors : void 0,\n signals: [],\n timestamp: getTimestamp8(this._recorder)\n };\n }\n this._recorder.recordAction(action);\n this._recorder.setMode(\"recording\");\n (_b = this._recorder.overlay) == null ? void 0 : _b.flashToolSucceeded(\"recordingArea\");\n }\n};\n\n// packages/injected/src/recorder/skyramp/domSnapshotTool.ts\nfunction getTimestamp9(recorder) {\n return recorder.injectedScript.utils.builtins.Date.now().toString();\n}\nvar DomSnapshotTool = class {\n constructor(recorder) {\n this._actionSequenceCounter = 0;\n this._recorder = recorder;\n }\n cursor() {\n return \"default\";\n }\n install() {\n this._captureDomSnapshot();\n }\n uninstall() {\n }\n cleanup() {\n }\n _captureDomSnapshot() {\n const snapshot = this._serializeDom();\n const action = {\n name: \"domSnapshot\",\n snapshotData: snapshot,\n signals: [],\n timestamp: getTimestamp9(this._recorder)\n };\n this._recorder.recordAction(action);\n this._recorder.setMode(\"recording\");\n }\n _serializeDom() {\n const doc = this._recorder.document;\n const win = this._recorder.injectedScript.window;\n return {\n url: doc.location.href,\n timestamp: Date.now(),\n viewport: {\n width: win.innerWidth,\n height: win.innerHeight\n },\n dom: this._buildDomTree(doc.documentElement),\n metadata: {\n actionSequence: this._actionSequenceCounter++,\n sessionId: `rec-${Date.now()}`\n }\n };\n }\n _buildDomTree(element, depth = 0) {\n var _a, _b;\n if (depth > 50)\n return null;\n const tagName = element.tagName.toLowerCase();\n if (tagName.startsWith(\"x-pw-\"))\n return null;\n const node = {\n tag: tagName,\n attributes: this._getAttributes(element)\n };\n if (element.childNodes.length === 0 || element.childNodes.length === 1 && ((_a = element.firstChild) == null ? void 0 : _a.nodeType) === 3) {\n const text = (_b = element.textContent) == null ? void 0 : _b.trim();\n if (text)\n node.text = text;\n }\n if (element instanceof HTMLInputElement) {\n node.value = element.value;\n node.checked = element.checked;\n node.type = element.type;\n } else if (element instanceof HTMLTextAreaElement) {\n node.value = element.value;\n } else if (element instanceof HTMLSelectElement) {\n node.value = element.value;\n node.selectedOptions = Array.from(element.selectedOptions).map((o) => o.value);\n }\n if (element.shadowRoot) {\n const shadowChildren = Array.from(element.shadowRoot.children).map((child) => this._buildDomTree(child, depth + 1)).filter(Boolean);\n if (shadowChildren.length > 0)\n node.shadowRoot = shadowChildren;\n }\n const children = Array.from(element.children).map((child) => this._buildDomTree(child, depth + 1)).filter(Boolean);\n if (children.length > 0)\n node.children = children;\n return node;\n }\n _getAttributes(element) {\n const attrs = {};\n const priorityAttrs = [\n \"id\",\n \"class\",\n \"name\",\n \"type\",\n \"role\",\n \"aria-label\",\n \"aria-describedby\",\n \"aria-labelledby\",\n \"data-testid\",\n \"data-test-id\",\n \"data-test\",\n \"placeholder\",\n \"title\",\n \"alt\",\n \"href\",\n \"src\",\n \"value\",\n \"for\",\n \"action\",\n \"method\"\n ];\n for (const attr of priorityAttrs) {\n const value = element.getAttribute(attr);\n if (value)\n attrs[attr] = value;\n }\n return attrs;\n }\n};\n\n// packages/injected/src/recorder/skyramp/modalUtils.ts\nvar MIN_BLOCKING_Z_INDEX = 1e3;\nvar MIN_HIGH_PRIORITY_Z_INDEX = 9999;\nfunction hideModalForAssertion() {\n try {\n const modalExists = document.querySelector(\"#modal-root dialog, #modal-root .modal_root\");\n if (modalExists) {\n const script = `\n (() => {\n const S = (window.__pwHideModal__ ||= {});\n const MIN_BLOCKING_Z_INDEX = ${MIN_BLOCKING_Z_INDEX};\n const MIN_HIGH_PRIORITY_Z_INDEX = ${MIN_HIGH_PRIORITY_Z_INDEX};\n\n // Find ALL dialogs in the modal (main modal + any dropdowns)\n const dialogs = Array.from(document.querySelectorAll('#modal-root dialog, #modal-root .modal_root'));\n if (!dialogs.length) return console.warn('No modal dialogs found.');\n\n if (S.hidden) return console.log('Already hidden.');\n\n // Remember initial state for ALL dialogs\n S.dialogs = dialogs.map(dlg => ({\n element: dlg,\n wasModal: typeof HTMLDialogElement !== 'undefined'\n && dlg instanceof HTMLDialogElement\n && dlg.matches(':modal')\n }));\n\n // Prevent the app from reacting to close/cancel while we hide all dialogs\n S.stopper = e => e.stopImmediatePropagation();\n S.dialogs.forEach(({ element }) => {\n element.addEventListener('close', S.stopper, true);\n element.addEventListener('cancel', S.stopper, true);\n });\n\n // Release the top layer without letting the app know for ALL dialogs\n S.dialogs.forEach(({ element, wasModal }) => {\n try {\n if (wasModal && typeof element.close === 'function') element.close('pw-temp-hide');\n else element.removeAttribute('open');\n } catch {}\n });\n\n // Visually/interaction-wise hide the whole modal container\n const root = document.getElementById('modal-root') || S.dialogs[0]?.element.closest('#modal-root') || S.dialogs[0]?.element;\n S.root = root;\n S.prevVis = root.style.visibility;\n S.prevPE = root.style.pointerEvents;\n root.style.visibility = 'hidden';\n root.style.pointerEvents = 'none';\n\n // Common \"page lock\" cleanups (store and undo later)\n S.bodyOverflow = document.body.style.overflow;\n document.body.style.overflow = '';\n\n S.inertEls = Array.from(document.querySelectorAll('[inert]'));\n S.inertEls.forEach(el => el.removeAttribute('inert'));\n\n S.ariaHidden = [];\n Array.from(document.body.children).forEach(el => {\n if (el === root) return;\n const v = el.getAttribute('aria-hidden');\n if (v !== null) { S.ariaHidden.push([el, v]); el.removeAttribute('aria-hidden'); }\n });\n\n // Find and neutralize ALL blocking elements, especially dropdown-related overlays\n S.tempPeNone = [];\n\n // Check multiple points across the screen for blocking elements\n const testPoints = [\n [innerWidth/2, innerHeight/2], // center\n [innerWidth/4, innerHeight/4], // top-left\n [3*innerWidth/4, innerHeight/4], // top-right\n [innerWidth/4, 3*innerHeight/4], // bottom-left\n [3*innerWidth/4, 3*innerHeight/4], // bottom-right\n [innerWidth/2, innerHeight/4], // top-center\n [innerWidth/2, 3*innerHeight/4], // bottom-center\n ];\n\n testPoints.forEach(([x, y]) => {\n const probe = document.elementFromPoint(x, y);\n if (probe && probe !== root && !root.contains(probe) &&\n !probe.tagName?.toLowerCase().startsWith('x-pw-') &&\n probe.id !== 'x-pw-glass' &&\n !S.tempPeNone.includes(probe)) {\n\n const cs = getComputedStyle(probe);\n const isBlocking = (\n // Original full-screen check\n (cs.position === 'fixed' &&\n cs.top === '0px' && cs.left === '0px' && cs.right === '0px' && cs.bottom === '0px') ||\n // Dropdown overlay patterns\n (cs.position === 'fixed' && parseInt(cs.zIndex) > MIN_BLOCKING_Z_INDEX) ||\n (cs.position === 'absolute' && parseInt(cs.zIndex) > MIN_BLOCKING_Z_INDEX) ||\n // Common backdrop patterns\n (cs.position === 'fixed' && cs.inset === '0px') ||\n // Elements that cover significant area\n (cs.position === 'fixed' && cs.width && cs.height &&\n parseInt(cs.width) > innerWidth/2 && parseInt(cs.height) > innerHeight/2)\n );\n\n if (isBlocking) {\n S.tempPeNone.push(probe);\n probe.style.pointerEvents = 'none';\n console.log('Disabled blocking element at', x, y, ':', probe, 'z-index:', cs.zIndex);\n }\n }\n });\n\n // Also scan for high z-index elements that might be blocking\n const highZElements = Array.from(document.querySelectorAll('*')).filter(el => {\n if (el === root || root.contains(el) ||\n el.tagName?.toLowerCase().startsWith('x-pw-') ||\n S.tempPeNone.includes(el)) return false;\n\n const cs = getComputedStyle(el);\n return cs.zIndex && parseInt(cs.zIndex) > MIN_HIGH_PRIORITY_Z_INDEX;\n });\n\n highZElements.forEach(el => {\n S.tempPeNone.push(el);\n el.style.pointerEvents = 'none';\n console.log('Disabled high z-index element:', el, 'z-index:', getComputedStyle(el).zIndex);\n });\n\n S.hidden = true;\n console.log('\\u2705 Modal hidden automatically for text assertion.');\n })();\n `;\n new Function(script)();\n }\n } catch (e) {\n console.warn(\"Failed to auto-hide modal for assertion:\", e);\n }\n}\nfunction showModalAfterAssertion() {\n try {\n const script = `\n (() => {\n const S = window.__pwHideModal__;\n if (!S?.hidden) return console.warn('Nothing to restore.');\n\n const { dialogs, root } = S;\n if (!dialogs?.length || !root || !document.contains(root)) {\n return console.warn('Modal root/dialogs no longer in DOM (app removed it).');\n }\n\n // Make container visible/clickable again\n root.style.visibility = S.prevVis ?? '';\n root.style.pointerEvents = S.prevPE ?? '';\n\n // Bring ALL dialogs back into the top layer (if they were modal)\n dialogs.forEach(({ element, wasModal }) => {\n try {\n if (wasModal && typeof element.showModal === 'function') element.showModal();\n else element.setAttribute('open', '');\n } catch (e) {\n // Fallback: at least show it\n element.setAttribute('open', '');\n }\n });\n\n // Re-apply page locks as they were\n if (S.bodyOverflow !== undefined) document.body.style.overflow = S.bodyOverflow;\n (S.inertEls || []).forEach(el => el.setAttribute('inert', ''));\n (S.ariaHidden || []).forEach(([el, v]) => el.setAttribute('aria-hidden', v));\n (S.tempPeNone || []).forEach(el => el.style.removeProperty('pointer-events'));\n\n // Allow the app to receive close/cancel in the future for ALL dialogs\n if (S.stopper) {\n dialogs.forEach(({ element }) => {\n element.removeEventListener('close', S.stopper, true);\n element.removeEventListener('cancel', S.stopper, true);\n });\n }\n\n S.hidden = false;\n console.log('\\u2705 Modal restored automatically after text assertion.');\n })();\n `;\n new Function(script)();\n } catch (e) {\n console.warn(\"Failed to auto-show modal after assertion:\", e);\n }\n}\n\n// packages/injected/src/recorder/skyramp/modalHandler.ts\nfunction log2(...args) {\n if (typeof window !== \"undefined\" && window.__SKYRAMP_DEBUG__)\n console.log(\"[ModalHandler]\", ...args);\n}\nfunction buildSelector(element) {\n const testId = element.getAttribute(\"data-testid\");\n if (testId)\n return `dialog[data-testid=\"${testId}\"]`;\n if (element.id)\n return `#${element.id}`;\n const ariaLabel = element.getAttribute(\"aria-label\");\n if (ariaLabel)\n return `dialog[aria-label=\"${ariaLabel}\"]`;\n const tag = element.tagName.toLowerCase();\n const cls = element.className;\n if (typeof cls === \"string\" && cls.trim())\n return `${tag}.${cls.trim().split(/\\s+/).join(\".\")}`;\n return tag;\n}\nvar ModalHandler = class {\n constructor(document2) {\n this._observer = null;\n this._enabled = false;\n this._activeModal = null;\n this._document = document2;\n }\n setOnModalOpen(cb) {\n this._onModalOpen = cb;\n }\n setOnModalClose(cb) {\n this._onModalClose = cb;\n }\n isModalOpen() {\n return this._activeModal !== null;\n }\n enable() {\n if (this._enabled)\n return;\n this._enabled = true;\n if (!this._document.body)\n return;\n this._observer = new MutationObserver((mutations) => this._handleMutations(mutations));\n this._observer.observe(this._document.body, {\n childList: true,\n subtree: true,\n attributes: true,\n attributeFilter: [\"open\", \"aria-modal\", \"class\"]\n });\n this._checkExistingModals();\n log2(\"Enabled \\u2014 watching for modal lifecycle events\");\n }\n disable() {\n if (!this._enabled)\n return;\n this._enabled = false;\n if (this._observer) {\n this._observer.disconnect();\n this._observer = null;\n }\n this._activeModal = null;\n log2(\"Disabled\");\n }\n _checkExistingModals() {\n const openDialog = this._document.querySelector(\"dialog[open]\");\n if (openDialog) {\n this._emitOpen(openDialog);\n return;\n }\n const ariaModal = this._document.querySelector('[aria-modal=\"true\"]');\n if (ariaModal) {\n this._emitOpen(ariaModal);\n return;\n }\n const carbonModal = this._document.querySelector(\".cds--modal.is-visible\");\n if (carbonModal) {\n this._emitOpen(carbonModal);\n return;\n }\n }\n _handleMutations(mutations) {\n var _a, _b, _c;\n if (!this._enabled)\n return;\n for (const mutation of mutations) {\n if (mutation.type === \"attributes\") {\n const target = mutation.target;\n if (mutation.attributeName === \"open\" && target.tagName === \"DIALOG\") {\n if (target.hasAttribute(\"open\"))\n this._emitOpen(target);\n else\n this._emitClose(target);\n continue;\n }\n if (mutation.attributeName === \"aria-modal\") {\n if (target.getAttribute(\"aria-modal\") === \"true\")\n this._emitOpen(target);\n else if (this._activeModal === target)\n this._emitClose(target);\n continue;\n }\n if (mutation.attributeName === \"class\" && ((_a = target.classList) == null ? void 0 : _a.contains(\"cds--modal\"))) {\n if (target.classList.contains(\"is-visible\"))\n this._emitOpen(target);\n else if (this._activeModal === target)\n this._emitClose(target);\n continue;\n }\n }\n if (mutation.type === \"childList\") {\n for (const node of mutation.addedNodes) {\n if (!(node instanceof Element))\n continue;\n if (node.tagName === \"DIALOG\" && node.hasAttribute(\"open\"))\n this._emitOpen(node);\n else if (((_b = node.getAttribute) == null ? void 0 : _b.call(node, \"aria-modal\")) === \"true\")\n this._emitOpen(node);\n else if (((_c = node.classList) == null ? void 0 : _c.contains(\"cds--modal\")) && node.classList.contains(\"is-visible\"))\n this._emitOpen(node);\n }\n if (this._activeModal) {\n for (const node of mutation.removedNodes) {\n if (node === this._activeModal || node instanceof Element && node.contains(this._activeModal))\n this._emitClose(this._activeModal);\n }\n }\n }\n }\n }\n _emitOpen(element) {\n var _a;\n if (this._activeModal === element)\n return;\n this._activeModal = element;\n const selector = buildSelector(element);\n log2(\"Modal opened:\", selector);\n (_a = this._onModalOpen) == null ? void 0 : _a.call(this, { selector });\n }\n _emitClose(element) {\n var _a;\n if (this._activeModal !== element)\n return;\n const selector = buildSelector(element);\n this._activeModal = null;\n log2(\"Modal closed:\", selector);\n (_a = this._onModalClose) == null ? void 0 : _a.call(this, { selector });\n }\n};\n\n// packages/injected/src/recorder/skyramp/iframeHandler.ts\nfunction log3(...args) {\n if (typeof window !== \"undefined\" && window.__SKYRAMP_DEBUG__)\n console.log(\"[IframeHandler]\", ...args);\n}\nfunction isDynamicIframeId(id) {\n if (/\\d{2,}[_-][a-zA-Z]/.test(id)) return true;\n if (/[-_]\\d+$/.test(id)) return true;\n if (/^react-aria\\d+/.test(id)) return true;\n if (/^(mui|mat|cdk)-\\d+/.test(id)) return true;\n if (/^\\d+$/.test(id)) return true;\n if (/\\d{4,}/.test(id)) return true;\n if (id.includes(\":\")) return true;\n return false;\n}\nfunction buildIframeSelector(iframe) {\n if (iframe.title)\n return `iframe[title=\"${iframe.title}\"]`;\n if (iframe.name)\n return `iframe[name=\"${iframe.name}\"]`;\n if (iframe.id && !isDynamicIframeId(iframe.id))\n return `#${iframe.id}`;\n if (iframe.src) {\n try {\n const url = new URL(iframe.src);\n if ((url.protocol === \"http:\" || url.protocol === \"https:\") && url.pathname && url.pathname !== \"/\") {\n return `iframe[src*=\"${url.pathname}\"]`;\n }\n } catch {\n }\n return `iframe[src=\"${iframe.src}\"]`;\n }\n if (iframe.id)\n return `#${iframe.id}`;\n return \"iframe\";\n}\nvar IframeHandler = class {\n constructor(document2) {\n this._observer = null;\n this._enabled = false;\n this._trackedIframes = /* @__PURE__ */ new WeakSet();\n this._document = document2;\n }\n setOnIframeLoad(cb) {\n this._onIframeLoad = cb;\n }\n enable() {\n if (this._enabled)\n return;\n this._enabled = true;\n if (!this._document.body)\n return;\n this._observer = new MutationObserver((mutations) => this._handleMutations(mutations));\n this._observer.observe(this._document.body, {\n childList: true,\n subtree: true\n });\n this._trackExistingIframes();\n console.log(\"[IframeHandler] Enabled \\u2014 watching for iframe load events\");\n }\n disable() {\n if (!this._enabled)\n return;\n this._enabled = false;\n if (this._observer) {\n this._observer.disconnect();\n this._observer = null;\n }\n log3(\"Disabled\");\n }\n _trackExistingIframes() {\n for (const iframe of this._document.querySelectorAll(\"iframe\"))\n this._trackIframe(iframe);\n }\n _handleMutations(mutations) {\n if (!this._enabled)\n return;\n for (const mutation of mutations) {\n if (mutation.type !== \"childList\")\n continue;\n for (const node of mutation.addedNodes) {\n if (node instanceof HTMLIFrameElement)\n this._trackIframe(node);\n if (node instanceof Element) {\n for (const iframe of node.querySelectorAll(\"iframe\"))\n this._trackIframe(iframe);\n }\n }\n }\n }\n _trackIframe(iframe) {\n var _a, _b;\n if (this._trackedIframes.has(iframe))\n return;\n this._trackedIframes.add(iframe);\n iframe.addEventListener(\"load\", () => {\n var _a2;\n if (!this._enabled)\n return;\n const selector = buildIframeSelector(iframe);\n console.log(\"[IframeHandler] Iframe loaded:\", selector);\n (_a2 = this._onIframeLoad) == null ? void 0 : _a2.call(this, { selector });\n }, { once: true });\n try {\n if (((_a = iframe.contentDocument) == null ? void 0 : _a.readyState) === \"complete\") {\n const selector = buildIframeSelector(iframe);\n console.log(\"[IframeHandler] Iframe already loaded:\", selector);\n (_b = this._onIframeLoad) == null ? void 0 : _b.call(this, { selector });\n }\n } catch {\n }\n }\n};\n\n// packages/injected/src/recorder/recorder.ts\nvar HighlightColors2 = {\n multiple: \"#f6b26b7f\",\n single: \"#6fa8dc7f\",\n assert: \"#8acae480\",\n action: \"#dc6f6f7f\",\n snapshot: \"#9c7fe480\"\n // Purple for visual snapshots\n};\nfunction computeScopedSelector(injectedScript, element, testIdAttributeName) {\n const elementInfo = injectedScript.generateSelector(element, { testIdAttributeName });\n let selector = elementInfo.selector;\n let scoped;\n const scopingResult = applyScopingHook(injectedScript, element, elementInfo.selector, elementInfo.elements);\n if (scopingResult) {\n selector = scopingResult.selector;\n if (!scopingResult.isFormContainer && !scopingResult.usesTextFilter) {\n scoped = {\n container: scopingResult.containerSelector,\n index: scopingResult.containerIndex,\n relative: scopingResult.relativeSelector\n };\n }\n }\n return { selector, scoped };\n}\nvar NoneTool = class {\n};\nvar InspectTool = class {\n constructor(recorder, assertVisibility) {\n this._hoveredModel = null;\n this._hoveredElement = null;\n this._recorder = recorder;\n this._assertVisibility = assertVisibility;\n }\n cursor() {\n return \"pointer\";\n }\n uninstall() {\n this._hoveredModel = null;\n this._hoveredElement = null;\n }\n onClick(event) {\n var _a;\n consumeEvent5(event);\n if (event.button !== 0)\n return;\n if ((_a = this._hoveredModel) == null ? void 0 : _a.selector)\n this._commit(this._hoveredModel.selector, this._hoveredModel);\n }\n onPointerDown(event) {\n consumeEvent5(event);\n }\n onPointerUp(event) {\n consumeEvent5(event);\n }\n onMouseDown(event) {\n consumeEvent5(event);\n }\n onMouseUp(event) {\n consumeEvent5(event);\n }\n onMouseMove(event) {\n var _a;\n consumeEvent5(event);\n let target = this._recorder.deepEventTarget(event);\n if (!target.isConnected)\n target = null;\n if (this._hoveredElement === target)\n return;\n this._hoveredElement = target;\n let model = null;\n if (this._hoveredElement) {\n const generated = this._recorder.injectedScript.generateSelector(this._hoveredElement, { testIdAttributeName: this._recorder.state.testIdAttributeName, multiple: false });\n const scopingResult = applyScopingHook(\n this._recorder.injectedScript,\n this._hoveredElement,\n generated.selector,\n generated.elements\n );\n const finalSelector = scopingResult ? scopingResult.selector : generated.selector;\n const finalElements = scopingResult ? scopingResult.elements : generated.elements;\n model = {\n selector: finalSelector,\n elements: finalElements,\n tooltipText: this._recorder.injectedScript.utils.asLocator(this._recorder.state.language, finalSelector),\n color: this._assertVisibility ? HighlightColors2.assert : HighlightColors2.single\n };\n }\n if (((_a = this._hoveredModel) == null ? void 0 : _a.selector) === (model == null ? void 0 : model.selector))\n return;\n this._hoveredModel = model;\n this._recorder.updateHighlight(model, true);\n }\n onMouseEnter(event) {\n consumeEvent5(event);\n }\n onMouseLeave(event) {\n consumeEvent5(event);\n const window2 = this._recorder.injectedScript.window;\n if (window2.top !== window2 && this._recorder.deepEventTarget(event).nodeType === Node.DOCUMENT_NODE)\n this._reset(true);\n }\n onKeyDown(event) {\n consumeEvent5(event);\n if (event.key === \"Escape\") {\n if (this._assertVisibility)\n this._recorder.setMode(\"recording\");\n }\n }\n onKeyUp(event) {\n consumeEvent5(event);\n }\n onScroll(event) {\n this._reset(false);\n }\n _commit(selector, model) {\n var _a;\n if (this._assertVisibility) {\n this._recorder.recordAction({\n name: \"assertVisible\",\n selector,\n signals: [],\n timestamp: getTimestamp10(this._recorder)\n });\n this._recorder.setMode(\"recording\");\n (_a = this._recorder.overlay) == null ? void 0 : _a.flashToolSucceeded(\"assertingVisibility\");\n } else {\n this._recorder.elementPicked(selector, model);\n }\n }\n _reset(userGesture) {\n this._hoveredElement = null;\n this._hoveredModel = null;\n this._recorder.updateHighlight(null, userGesture);\n }\n};\nvar RecordActionTool = class {\n constructor(recorder) {\n this._hoveredModel = null;\n this._hoveredElement = null;\n this._activeModel = null;\n this._expectProgrammaticKeyUp = false;\n this._observer = null;\n this._recorder = recorder;\n this._performingActions = /* @__PURE__ */ new Set();\n this._dialog = new Dialog(recorder);\n }\n cursor() {\n return \"pointer\";\n }\n _installObserverIfNeeded() {\n var _a;\n if (this._observer)\n return;\n if (!((_a = this._recorder.injectedScript.document) == null ? void 0 : _a.body))\n return;\n this._observer = new MutationObserver((mutations) => {\n if (!this._hoveredElement)\n return;\n for (const mutation of mutations) {\n for (const node of mutation.removedNodes) {\n if (node === this._hoveredElement || node.contains(this._hoveredElement))\n this._resetHoveredModel();\n }\n }\n });\n this._observer.observe(this._recorder.injectedScript.document.body, { childList: true, subtree: true });\n }\n uninstall() {\n var _a;\n (_a = this._observer) == null ? void 0 : _a.disconnect();\n this._observer = null;\n this._hoveredModel = null;\n this._hoveredElement = null;\n this._activeModel = null;\n this._expectProgrammaticKeyUp = false;\n this._dialog.close();\n }\n onClick(event) {\n var _a, _b, _c;\n this._lastClickX = event.clientX;\n this._lastClickY = event.clientY;\n if (this._dialog.isShowing()) {\n if (event.button === 2 && event.type === \"auxclick\") {\n consumeEvent5(event);\n }\n return;\n }\n if (isRangeInput(this._hoveredElement))\n return;\n if (this._shouldIgnoreMouseEvent(event))\n return;\n const target = this._recorder.deepEventTarget(event);\n const fileInput = this._findFileInput(target);\n if (fileInput) {\n if (!this._activeModel) {\n const selector = (_b = (_a = this._hoveredModel) == null ? void 0 : _a.selector) != null ? _b : this._recorder.injectedScript.generateSelector(fileInput, { testIdAttributeName: this._recorder.state.testIdAttributeName }).selector;\n this._activeModel = (_c = this._hoveredModel) != null ? _c : { selector, elements: [fileInput], color: \"#dc6f6f7f\" };\n }\n return;\n }\n if (this._actionInProgress(event))\n return;\n if (this._consumedDueToNoModel(event, this._hoveredModel))\n return;\n if (event.button === 2 && event.type === \"auxclick\") {\n this._showActionListDialog(this._hoveredModel, event);\n return;\n }\n const checkbox = asCheckbox(this._recorder.deepEventTarget(event));\n if (checkbox && event.detail === 1) {\n this._performAction({\n name: checkbox.checked ? \"check\" : \"uncheck\",\n selector: this._hoveredModel.selector,\n signals: [],\n timestamp: getTimestamp10(this._recorder)\n });\n return;\n }\n this._cancelPendingClickAction();\n let targetSelector = this._hoveredModel.selector;\n let shouldAutoDisableNestedTool = false;\n if (this._recorder.pointerEventsOverrideEnabled) {\n const clickedElement = this._recorder.deepEventTarget(event);\n const nestedResult = this._recorder._nestedElementHandler.handleNestedClick(\n clickedElement,\n this._hoveredModel,\n this._recorder.injectedScript,\n this._recorder.state.testIdAttributeName\n );\n if (nestedResult) {\n targetSelector = nestedResult.targetSelector;\n shouldAutoDisableNestedTool = nestedResult.shouldAutoDisable;\n }\n }\n let inSubFrame;\n try {\n inSubFrame = window !== window.top;\n } catch {\n inSubFrame = true;\n }\n if (event.detail === 1) {\n const clickAction = {\n name: \"click\",\n selector: targetSelector,\n position: positionForEvent(event),\n signals: [],\n button: buttonForEvent(event),\n modifiers: modifiersForEvent(event),\n clickCount: event.detail,\n timestamp: getTimestamp10(this._recorder)\n };\n this._pendingClickAction = {\n action: clickAction,\n autoDisableNestedTool: shouldAutoDisableNestedTool,\n timeout: inSubFrame ? 0 : this._recorder.injectedScript.utils.builtins.setTimeout(() => this._commitPendingClickAction(), 200)\n };\n if (inSubFrame)\n this._commitPendingClickAction();\n }\n }\n onDblClick(event) {\n if (this._dialog.isShowing())\n return;\n if (isRangeInput(this._hoveredElement))\n return;\n if (this._shouldIgnoreMouseEvent(event))\n return;\n if (this._actionInProgress(event))\n return;\n if (this._consumedDueToNoModel(event, this._hoveredModel))\n return;\n this._cancelPendingClickAction();\n this._performAction({\n name: \"click\",\n selector: this._hoveredModel.selector,\n position: positionForEvent(event),\n signals: [],\n button: buttonForEvent(event),\n modifiers: modifiersForEvent(event),\n clickCount: event.detail,\n timestamp: getTimestamp10(this._recorder)\n });\n if (this._recorder.pointerEventsOverrideEnabled) {\n this._recorder.togglePointerEventsOverride();\n }\n }\n _commitPendingClickAction() {\n if (this._pendingClickAction) {\n this._performAction(this._pendingClickAction.action);\n if (this._pendingClickAction.autoDisableNestedTool && this._recorder.pointerEventsOverrideEnabled) {\n this._recorder.togglePointerEventsOverride();\n }\n }\n this._cancelPendingClickAction();\n }\n _cancelPendingClickAction() {\n if (this._pendingClickAction)\n this._recorder.injectedScript.utils.builtins.clearTimeout(this._pendingClickAction.timeout);\n this._pendingClickAction = void 0;\n }\n onContextMenu(event) {\n if (this._dialog.isShowing()) {\n consumeEvent5(event);\n return;\n }\n if (this._shouldIgnoreMouseEvent(event))\n return;\n if (this._actionInProgress(event))\n return;\n if (this._consumedDueToNoModel(event, this._hoveredModel))\n return;\n this._showActionListDialog(this._hoveredModel, event);\n }\n onPointerDown(event) {\n if (this._dialog.isShowing())\n return;\n if (this._shouldIgnoreMouseEvent(event))\n return;\n this._consumeWhenAboutToPerform(event);\n }\n onPointerUp(event) {\n if (this._dialog.isShowing())\n return;\n if (this._shouldIgnoreMouseEvent(event))\n return;\n this._consumeWhenAboutToPerform(event);\n }\n onMouseDown(event) {\n if (this._dialog.isShowing())\n return;\n if (this._shouldIgnoreMouseEvent(event))\n return;\n this._consumeWhenAboutToPerform(event);\n this._activeModel = this._hoveredModel;\n }\n onMouseUp(event) {\n if (this._dialog.isShowing())\n return;\n if (this._shouldIgnoreMouseEvent(event))\n return;\n this._consumeWhenAboutToPerform(event);\n }\n onMouseMove(event) {\n if (this._dialog.isShowing())\n return;\n const target = this._recorder.deepEventTarget(event);\n if (this._hoveredElement === target)\n return;\n this._hoveredElement = target;\n this._updateModelForHoveredElement();\n }\n onMouseLeave(event) {\n if (this._dialog.isShowing())\n return;\n const window2 = this._recorder.injectedScript.window;\n if (window2.top !== window2 && this._recorder.deepEventTarget(event).nodeType === Node.DOCUMENT_NODE) {\n this._hoveredElement = null;\n this._updateModelForHoveredElement();\n }\n }\n onFocus(event) {\n if (this._dialog.isShowing())\n return;\n this._onFocus(event.isTrusted);\n }\n onInput(event) {\n var _a, _b, _c, _d;\n if (this._dialog.isShowing())\n return;\n const target = this._recorder.deepEventTarget(event);\n if (target.nodeName === \"INPUT\" && target.type.toLowerCase() === \"file\") {\n const selector = (_b = (_a = this._activeModel) == null ? void 0 : _a.selector) != null ? _b : this._recorder.injectedScript.generateSelector(target, { testIdAttributeName: this._recorder.state.testIdAttributeName }).selector;\n const fileList = [...target.files || []];\n const files = fileList.map((file) => file.name);\n const webkitdirectory = target.webkitdirectory === true;\n const relativePaths = fileList.map((file) => file.webkitRelativePath || \"\");\n const triggerEl = (_d = (_c = this._activeModel) == null ? void 0 : _c.elements) == null ? void 0 : _d[0];\n const isDirectInputClick = !triggerEl || triggerEl === target;\n if (!isDirectInputClick) {\n this._recordAction({\n name: \"fileChooser\",\n selector,\n signals: [],\n files,\n webkitdirectory,\n relativePaths,\n timestamp: getTimestamp10(this._recorder)\n });\n return;\n }\n this._recordAction({\n name: \"setInputFiles\",\n selector,\n signals: [],\n files,\n webkitdirectory,\n relativePaths,\n timestamp: getTimestamp10(this._recorder)\n });\n return;\n }\n if (isRangeInput(target)) {\n this._recordAction({\n name: \"fill\",\n // must use hoveredModel instead of activeModel for it to work in webkit\n selector: this._hoveredModel.selector,\n signals: [],\n text: target.value,\n timestamp: getTimestamp10(this._recorder)\n });\n return;\n }\n if ([\"INPUT\", \"TEXTAREA\"].includes(target.nodeName) || target.isContentEditable) {\n if (target.nodeName === \"INPUT\" && [\"checkbox\", \"radio\"].includes(target.type.toLowerCase())) {\n return;\n }\n if (this._consumedDueWrongTarget(event))\n return;\n this._recordAction({\n name: \"fill\",\n selector: this._activeModel.selector,\n signals: [],\n text: target.isContentEditable ? target.innerText : target.value,\n // undefined rather than false so a non-password fill serializes exactly\n // as before (JSON.stringify drops undefined, as with ariaSnapshot).\n isPassword: isPasswordInput(target) ? true : void 0,\n timestamp: getTimestamp10(this._recorder)\n });\n }\n if (target.nodeName === \"SELECT\") {\n const selectElement = target;\n this._recordAction({\n name: \"select\",\n selector: this._activeModel.selector,\n options: [...selectElement.selectedOptions].map((option) => option.value),\n signals: [],\n timestamp: getTimestamp10(this._recorder)\n });\n }\n }\n onKeyDown(event) {\n if (this._dialog.isShowing())\n return;\n if (!this._shouldGenerateKeyPressFor(event))\n return;\n if (this._actionInProgress(event)) {\n this._expectProgrammaticKeyUp = true;\n return;\n }\n if (this._consumedDueWrongTarget(event))\n return;\n if (event.key === \" \") {\n const checkbox = asCheckbox(this._recorder.deepEventTarget(event));\n if (checkbox && event.detail === 0) {\n this._performAction({\n name: checkbox.checked ? \"uncheck\" : \"check\",\n selector: this._activeModel.selector,\n signals: [],\n timestamp: getTimestamp10(this._recorder)\n });\n return;\n }\n }\n this._performAction({\n name: \"press\",\n selector: this._activeModel.selector,\n signals: [],\n key: event.key,\n modifiers: modifiersForEvent(event),\n timestamp: getTimestamp10(this._recorder)\n });\n }\n onKeyUp(event) {\n if (this._dialog.isShowing())\n return;\n if (!this._shouldGenerateKeyPressFor(event))\n return;\n if (!this._expectProgrammaticKeyUp) {\n consumeEvent5(event);\n return;\n }\n this._expectProgrammaticKeyUp = false;\n }\n onScroll(event) {\n if (this._dialog.isShowing())\n return;\n this._resetHoveredModel();\n }\n _showActionListDialog(model, event) {\n consumeEvent5(event);\n const actionPosition = positionForEvent(event);\n const actions = [\n {\n title: \"Click\",\n cb: () => this._performAction({\n name: \"click\",\n selector: model.selector,\n position: actionPosition,\n signals: [],\n button: \"left\",\n modifiers: 0,\n clickCount: 1,\n timestamp: getTimestamp10(this._recorder)\n })\n },\n {\n title: \"Right click\",\n cb: () => this._performAction({\n name: \"click\",\n selector: model.selector,\n position: actionPosition,\n signals: [],\n button: \"right\",\n modifiers: 0,\n clickCount: 1,\n timestamp: getTimestamp10(this._recorder)\n })\n },\n {\n title: \"Double click\",\n cb: () => this._performAction({\n name: \"click\",\n selector: model.selector,\n position: actionPosition,\n signals: [],\n button: \"left\",\n modifiers: 0,\n clickCount: 2,\n timestamp: getTimestamp10(this._recorder)\n })\n },\n {\n title: \"Hover\",\n cb: () => this._performAction({\n name: \"hover\",\n selector: model.selector,\n position: actionPosition,\n signals: [],\n timestamp: getTimestamp10(this._recorder)\n })\n },\n {\n title: \"Pick locator\",\n cb: () => this._recorder.elementPicked(model.selector, model)\n }\n ];\n const listElement = this._recorder.document.createElement(\"x-pw-action-list\");\n listElement.setAttribute(\"role\", \"list\");\n listElement.setAttribute(\"aria-label\", \"Choose action\");\n for (const action of actions) {\n const actionElement = this._recorder.document.createElement(\"x-pw-action-item\");\n actionElement.setAttribute(\"role\", \"listitem\");\n actionElement.textContent = action.title;\n actionElement.setAttribute(\"aria-label\", action.title);\n actionElement.addEventListener(\"click\", () => {\n this._dialog.close();\n action.cb();\n });\n listElement.appendChild(actionElement);\n }\n const dialogElement = this._dialog.show({\n label: \"Choose action\",\n body: listElement,\n autosize: true\n });\n const anchorBox = this._recorder.highlight.firstTooltipBox() || model.elements[0].getBoundingClientRect();\n const dialogPosition = this._recorder.highlight.tooltipPosition(anchorBox, dialogElement);\n this._dialog.moveTo(dialogPosition.anchorTop, dialogPosition.anchorLeft);\n }\n _resetHoveredModel() {\n this._hoveredModel = null;\n this._hoveredElement = null;\n this._updateHighlight(false);\n }\n _onFocus(userGesture) {\n const activeElement = deepActiveElement(this._recorder.document);\n if (activeElement === this._recorder.document.body)\n return;\n const result = activeElement ? this._recorder.injectedScript.generateSelector(activeElement, { testIdAttributeName: this._recorder.state.testIdAttributeName }) : null;\n let finalSelector = result == null ? void 0 : result.selector;\n let finalElements = result == null ? void 0 : result.elements;\n if (activeElement && result) {\n const scopingResult = applyScopingHook(\n this._recorder.injectedScript,\n activeElement,\n result.selector,\n result.elements\n );\n if (scopingResult) {\n finalSelector = scopingResult.selector;\n finalElements = scopingResult.elements;\n }\n }\n this._activeModel = result && finalSelector ? { ...result, selector: finalSelector, elements: finalElements, color: HighlightColors2.action } : null;\n if (userGesture) {\n this._hoveredElement = activeElement;\n this._updateModelForHoveredElement();\n }\n }\n _shouldIgnoreMouseEvent(event) {\n const target = this._recorder.deepEventTarget(event);\n const nodeName = target.nodeName;\n if (nodeName === \"SELECT\" || nodeName === \"OPTION\")\n return true;\n if (nodeName === \"INPUT\" && [\"date\", \"range\"].includes(target.type))\n return true;\n return false;\n }\n _actionInProgress(event) {\n const isKeyEvent = event instanceof KeyboardEvent;\n const isMouseOrPointerEvent = event instanceof MouseEvent || event instanceof PointerEvent;\n for (const action of this._performingActions) {\n if (isKeyEvent && action.name === \"press\" && event.key === action.key)\n return true;\n if (isMouseOrPointerEvent && (action.name === \"click\" || action.name === \"hover\" || action.name === \"check\" || action.name === \"uncheck\"))\n return true;\n }\n consumeEvent5(event);\n return false;\n }\n _consumedDueToNoModel(event, model) {\n if (model)\n return false;\n consumeEvent5(event);\n return true;\n }\n _consumedDueWrongTarget(event) {\n if (this._activeModel && this._activeModel.elements[0] === this._recorder.deepEventTarget(event))\n return false;\n consumeEvent5(event);\n return true;\n }\n // Returns the file input that a click on `element` would activate, or\n // null if none. Only considers the element itself, a <label for=…>\n // pointing at a file input, or a file input within its descendants —\n // never ancestors, to avoid matching siblings of a file-input wrapper.\n _findFileInput(element) {\n if (element.nodeName === \"INPUT\" && element.type.toLowerCase() === \"file\")\n return element;\n if (element.nodeName === \"LABEL\") {\n const forId = element.htmlFor;\n if (forId) {\n const target = element.ownerDocument.getElementById(forId);\n if (target && target.nodeName === \"INPUT\" && target.type.toLowerCase() === \"file\")\n return target;\n }\n }\n const descendant = element.querySelector('input[type=\"file\"]');\n if (descendant)\n return descendant;\n return null;\n }\n _consumeWhenAboutToPerform(event) {\n if (!this._performingActions.size)\n consumeEvent5(event);\n }\n _recordAction(action) {\n this._recorder.recordAction(action);\n }\n _performAction(action) {\n this._recorder.updateHighlight(null, false);\n this._performingActions.add(action);\n const promise = this._recorder.performAction(action).then(() => {\n this._performingActions.delete(action);\n this._onFocus(false);\n });\n if (!this._recorder.injectedScript.isUnderTest)\n return;\n void promise.then(() => {\n console.error(\"Action performed for test: \" + JSON.stringify({\n // eslint-disable-line no-console\n hovered: this._hoveredModel ? this._hoveredModel.selector : null,\n active: this._activeModel ? this._activeModel.selector : null\n }));\n });\n }\n _shouldGenerateKeyPressFor(event) {\n if (typeof event.key !== \"string\")\n return false;\n if (event.key === \"Enter\" && (this._recorder.deepEventTarget(event).nodeName === \"TEXTAREA\" || this._recorder.deepEventTarget(event).isContentEditable))\n return false;\n if ([\"Backspace\", \"Delete\", \"AltGraph\"].includes(event.key))\n return false;\n if (event.key === \"@\" && event.code === \"KeyL\")\n return false;\n if (navigator.platform.includes(\"Mac\")) {\n if (event.key === \"v\" && event.metaKey)\n return false;\n } else {\n if (event.key === \"v\" && event.ctrlKey)\n return false;\n if (event.key === \"Insert\" && event.shiftKey)\n return false;\n }\n if ([\"Shift\", \"Control\", \"Meta\", \"Alt\", \"Process\"].includes(event.key))\n return false;\n const hasModifier = event.ctrlKey || event.altKey || event.metaKey;\n if (event.key.length === 1 && !hasModifier)\n return !!asCheckbox(this._recorder.deepEventTarget(event));\n return true;\n }\n _updateModelForHoveredElement() {\n this._installObserverIfNeeded();\n if (this._performingActions.size)\n return;\n if (!this._hoveredElement || !this._hoveredElement.isConnected) {\n this._hoveredModel = null;\n this._hoveredElement = null;\n this._updateHighlight(true);\n return;\n }\n let { selector, elements } = this._recorder.injectedScript.generateSelector(this._hoveredElement, {\n testIdAttributeName: this._recorder.state.testIdAttributeName\n });\n const scopingResult = applyScopingHook(\n this._recorder.injectedScript,\n this._hoveredElement,\n selector,\n elements\n );\n if (scopingResult) {\n selector = scopingResult.selector;\n elements = scopingResult.elements;\n }\n if (this._hoveredModel && this._hoveredModel.selector === selector)\n return;\n this._hoveredModel = selector ? { selector, elements, color: HighlightColors2.action } : null;\n this._updateHighlight(true);\n }\n _updateHighlight(userGesture) {\n this._recorder.updateHighlight(this._hoveredModel, userGesture);\n }\n};\nvar JsonRecordActionTool = class {\n constructor(recorder) {\n this._recorder = recorder;\n }\n install() {\n this._recorder.clearHighlight();\n }\n uninstall() {\n }\n onClick(event) {\n const element = this._recorder.deepEventTarget(event);\n if (isRangeInput(element))\n return;\n if (event.button === 2 && event.type === \"auxclick\")\n return;\n if (this._shouldIgnoreMouseEvent(event))\n return;\n const checkbox = asCheckbox(element);\n const { ariaSnapshot, selector, ref, scoped } = this._ariaSnapshot(element);\n if (checkbox && event.detail === 1) {\n this._recorder.recordAction({\n name: checkbox.checked ? \"check\" : \"uncheck\",\n selector,\n ref,\n scoped,\n signals: [],\n ariaSnapshot,\n timestamp: getTimestamp10(this._recorder)\n });\n return;\n }\n this._recorder.recordAction({\n name: \"click\",\n selector,\n ref,\n scoped,\n ariaSnapshot,\n position: positionForEvent(event),\n signals: [],\n button: buttonForEvent(event),\n modifiers: modifiersForEvent(event),\n clickCount: event.detail,\n timestamp: getTimestamp10(this._recorder)\n });\n }\n onContextMenu(event) {\n const element = this._recorder.deepEventTarget(event);\n const { ariaSnapshot, selector, ref, scoped } = this._ariaSnapshot(element);\n this._recorder.recordAction({\n name: \"click\",\n selector,\n ref,\n scoped,\n ariaSnapshot,\n position: positionForEvent(event),\n signals: [],\n button: \"right\",\n modifiers: modifiersForEvent(event),\n clickCount: 1,\n timestamp: getTimestamp10(this._recorder)\n });\n }\n onInput(event) {\n const element = this._recorder.deepEventTarget(event);\n const { ariaSnapshot, selector, ref, scoped } = this._ariaSnapshot(element);\n if (isRangeInput(element)) {\n this._recorder.recordAction({\n name: \"fill\",\n selector,\n ref,\n scoped,\n ariaSnapshot,\n signals: [],\n text: element.value,\n timestamp: getTimestamp10(this._recorder)\n });\n return;\n }\n if ([\"INPUT\", \"TEXTAREA\"].includes(element.nodeName) || element.isContentEditable) {\n if (element.nodeName === \"INPUT\" && [\"checkbox\", \"radio\"].includes(element.type.toLowerCase())) {\n return;\n }\n this._recorder.recordAction({\n name: \"fill\",\n ref,\n selector,\n scoped,\n ariaSnapshot,\n signals: [],\n text: element.isContentEditable ? element.innerText : element.value,\n // undefined rather than false so a non-password fill serializes exactly\n // as before (JSON.stringify drops undefined, as with ariaSnapshot).\n isPassword: isPasswordInput(element) ? true : void 0,\n timestamp: getTimestamp10(this._recorder)\n });\n return;\n }\n if (element.nodeName === \"SELECT\") {\n const selectElement = element;\n this._recorder.recordAction({\n name: \"select\",\n selector,\n ref,\n scoped,\n ariaSnapshot,\n options: [...selectElement.selectedOptions].map((option) => option.value),\n signals: [],\n timestamp: getTimestamp10(this._recorder)\n });\n return;\n }\n }\n onKeyDown(event) {\n if (!this._shouldGenerateKeyPressFor(event))\n return;\n const element = this._recorder.deepEventTarget(event);\n const { ariaSnapshot, selector, ref, scoped } = this._ariaSnapshot(element);\n if (event.key === \" \") {\n const checkbox = asCheckbox(element);\n if (checkbox && event.detail === 0) {\n this._recorder.recordAction({\n name: checkbox.checked ? \"uncheck\" : \"check\",\n selector,\n ref,\n scoped,\n ariaSnapshot,\n signals: [],\n timestamp: getTimestamp10(this._recorder)\n });\n return;\n }\n }\n this._recorder.recordAction({\n name: \"press\",\n selector,\n ref,\n scoped,\n ariaSnapshot,\n signals: [],\n key: event.key,\n modifiers: modifiersForEvent(event),\n timestamp: getTimestamp10(this._recorder)\n });\n }\n _shouldIgnoreMouseEvent(event) {\n const target = this._recorder.deepEventTarget(event);\n const nodeName = target.nodeName;\n if (nodeName === \"SELECT\" || nodeName === \"OPTION\")\n return true;\n if (nodeName === \"INPUT\" && [\"date\", \"range\"].includes(target.type))\n return true;\n return false;\n }\n _shouldGenerateKeyPressFor(event) {\n if (typeof event.key !== \"string\")\n return false;\n if (event.key === \"Enter\" && (this._recorder.deepEventTarget(event).nodeName === \"TEXTAREA\" || this._recorder.deepEventTarget(event).isContentEditable))\n return false;\n if ([\"Backspace\", \"Delete\", \"AltGraph\"].includes(event.key))\n return false;\n if (event.key === \"@\" && event.code === \"KeyL\")\n return false;\n if (navigator.platform.includes(\"Mac\")) {\n if (event.key === \"v\" && event.metaKey)\n return false;\n } else {\n if (event.key === \"v\" && event.ctrlKey)\n return false;\n if (event.key === \"Insert\" && event.shiftKey)\n return false;\n }\n if ([\"Shift\", \"Control\", \"Meta\", \"Alt\", \"Process\"].includes(event.key))\n return false;\n const hasModifier = event.ctrlKey || event.altKey || event.metaKey;\n if (event.key.length === 1 && !hasModifier)\n return !this._isEditable(this._recorder.deepEventTarget(event));\n return true;\n }\n _isEditable(element) {\n if (element.nodeName === \"TEXTAREA\" || element.nodeName === \"INPUT\")\n return true;\n if (element.isContentEditable)\n return true;\n return false;\n }\n _ariaSnapshot(element) {\n const { ariaSnapshot, refs } = this._recorder.injectedScript.ariaSnapshotForRecorder();\n const ref = element ? refs.get(element) : void 0;\n let finalSelector;\n let scoped;\n if (element) {\n const computed = computeScopedSelector(this._recorder.injectedScript, element, this._recorder.state.testIdAttributeName);\n finalSelector = computed.selector;\n scoped = computed.scoped;\n }\n return { ariaSnapshot, selector: finalSelector, ref, scoped };\n }\n};\nvar TextAssertionTool = class {\n constructor(recorder, kind) {\n this._hoverHighlight = null;\n this._action = null;\n this._recorder = recorder;\n this._textCache = /* @__PURE__ */ new Map();\n this._kind = kind;\n this._dialog = new Dialog(recorder);\n }\n cursor() {\n return \"pointer\";\n }\n uninstall() {\n this._dialog.close();\n this._hoverHighlight = null;\n }\n onClick(event) {\n consumeEvent5(event);\n if (this._kind === \"value\") {\n this._commitAssertValue();\n } else {\n if (!this._dialog.isShowing())\n this._showDialog();\n }\n }\n onMouseDown(event) {\n const target = this._recorder.deepEventTarget(event);\n if (this._elementHasValue(target))\n event.preventDefault();\n else\n consumeEvent5(event);\n }\n onPointerDown(event) {\n consumeEvent5(event);\n }\n onPointerUp(event) {\n var _a;\n const target = (_a = this._hoverHighlight) == null ? void 0 : _a.elements[0];\n if (this._kind === \"value\" && target && (target.nodeName === \"INPUT\" || target.nodeName === \"SELECT\") && target.disabled) {\n this._commitAssertValue();\n }\n }\n onMouseMove(event) {\n var _a;\n if (this._dialog.isShowing())\n return;\n const target = this._recorder.deepEventTarget(event);\n if (((_a = this._hoverHighlight) == null ? void 0 : _a.elements[0]) === target)\n return;\n if (this._kind === \"text\" || this._kind === \"snapshot\") {\n this._hoverHighlight = this._recorder.injectedScript.utils.elementText(this._textCache, target).full ? { elements: [target], selector: \"\", color: HighlightColors2.assert } : null;\n } else if (this._elementHasValue(target)) {\n const generated = this._recorder.injectedScript.generateSelector(target, { testIdAttributeName: this._recorder.state.testIdAttributeName });\n this._hoverHighlight = { selector: generated.selector, elements: generated.elements, color: HighlightColors2.assert };\n } else {\n this._hoverHighlight = null;\n }\n this._recorder.updateHighlight(this._hoverHighlight, true);\n }\n onKeyDown(event) {\n if (event.key === \"Escape\")\n this._recorder.setMode(\"recording\");\n consumeEvent5(event);\n }\n onScroll(event) {\n this._recorder.updateHighlight(this._hoverHighlight, false);\n }\n _elementHasValue(element) {\n return element.nodeName === \"TEXTAREA\" || element.nodeName === \"SELECT\" || element.nodeName === \"INPUT\" && ![\"button\", \"image\", \"reset\", \"submit\"].includes(element.type);\n }\n _generateAction() {\n var _a;\n this._textCache.clear();\n const target = (_a = this._hoverHighlight) == null ? void 0 : _a.elements[0];\n if (!target)\n return null;\n if (this._kind === \"value\") {\n if (!this._elementHasValue(target))\n return null;\n const { selector } = this._recorder.injectedScript.generateSelector(target, { testIdAttributeName: this._recorder.state.testIdAttributeName });\n if (target.nodeName === \"INPUT\" && [\"checkbox\", \"radio\"].includes(target.type.toLowerCase())) {\n return {\n name: \"assertChecked\",\n selector,\n signals: [],\n // Interestingly, inputElement.checked is reversed inside this event handler.\n checked: !target.checked,\n timestamp: getTimestamp10(this._recorder)\n };\n } else {\n return {\n name: \"assertValue\",\n selector,\n signals: [],\n value: target.value,\n timestamp: getTimestamp10(this._recorder)\n };\n }\n } else if (this._kind === \"snapshot\") {\n const generated = this._recorder.injectedScript.generateSelector(target, { testIdAttributeName: this._recorder.state.testIdAttributeName, forTextExpect: true });\n this._hoverHighlight = { selector: generated.selector, elements: generated.elements, color: HighlightColors2.assert };\n this._recorder.updateHighlight(this._hoverHighlight, true);\n return {\n name: \"assertSnapshot\",\n selector: this._hoverHighlight.selector,\n signals: [],\n ariaSnapshot: this._recorder.injectedScript.ariaSnapshot(target, { mode: \"codegen\" }),\n timestamp: getTimestamp10(this._recorder)\n };\n } else {\n const closestTd = target.closest(\"td\");\n const isInTableCell = closestTd && closestTd.closest(\"tr\");\n let generated = this._recorder.injectedScript.generateSelector(target, {\n testIdAttributeName: this._recorder.state.testIdAttributeName,\n forTextExpect: !isInTableCell\n });\n const scopingResult = applyScopingHook(\n this._recorder.injectedScript,\n target,\n generated.selector,\n generated.elements\n );\n if (scopingResult) {\n generated = { selector: scopingResult.selector, selectors: [scopingResult.selector], elements: scopingResult.elements };\n }\n this._hoverHighlight = { selector: generated.selector, elements: generated.elements, color: HighlightColors2.assert };\n this._recorder.updateHighlight(this._hoverHighlight, true);\n return {\n name: \"assertText\",\n selector: this._hoverHighlight.selector,\n signals: [],\n text: this._recorder.injectedScript.utils.elementText(this._textCache, target).normalized,\n substring: true,\n timestamp: getTimestamp10(this._recorder)\n };\n }\n }\n _renderValue(action) {\n if ((action == null ? void 0 : action.name) === \"assertText\")\n return this._recorder.injectedScript.utils.normalizeWhiteSpace(action.text);\n if ((action == null ? void 0 : action.name) === \"assertChecked\")\n return String(action.checked);\n if ((action == null ? void 0 : action.name) === \"assertValue\")\n return action.value;\n if ((action == null ? void 0 : action.name) === \"assertSnapshot\")\n return action.ariaSnapshot;\n return \"\";\n }\n _commit() {\n if (!this._action || !this._dialog.isShowing())\n return;\n this._dialog.close();\n this._recorder.recordAction(this._action);\n this._recorder.setMode(\"recording\");\n showModalAfterAssertion();\n }\n _showDialog() {\n var _a, _b, _c, _d;\n if (!((_a = this._hoverHighlight) == null ? void 0 : _a.elements[0]))\n return;\n hideModalForAssertion();\n this._action = this._generateAction();\n if (((_b = this._action) == null ? void 0 : _b.name) === \"assertText\") {\n this._showTextDialog(this._action);\n } else if (((_c = this._action) == null ? void 0 : _c.name) === \"assertSnapshot\") {\n this._recorder.recordAction(this._action);\n this._recorder.setMode(\"recording\");\n (_d = this._recorder.overlay) == null ? void 0 : _d.flashToolSucceeded(\"assertingSnapshot\");\n }\n }\n _showTextDialog(action) {\n const textElement = this._recorder.document.createElement(\"textarea\");\n textElement.setAttribute(\"spellcheck\", \"false\");\n textElement.value = this._renderValue(action);\n textElement.classList.add(\"text-editor\");\n const updateAndValidate = () => {\n var _a;\n const newValue = this._recorder.injectedScript.utils.normalizeWhiteSpace(textElement.value);\n const target = (_a = this._hoverHighlight) == null ? void 0 : _a.elements[0];\n if (!target)\n return;\n action.text = newValue;\n const targetText = this._recorder.injectedScript.utils.elementText(this._textCache, target).normalized;\n const matches = newValue && targetText.includes(newValue);\n textElement.classList.toggle(\"does-not-match\", !matches);\n };\n textElement.addEventListener(\"input\", updateAndValidate);\n const label = \"Assert that element contains text\";\n const dialogElement = this._dialog.show({\n label,\n body: textElement,\n onCommit: () => this._commit()\n });\n const position = this._recorder.highlight.tooltipPosition(this._recorder.highlight.firstBox(), dialogElement);\n this._dialog.moveTo(position.anchorTop, position.anchorLeft);\n textElement.focus();\n }\n _commitAssertValue() {\n var _a;\n if (this._kind !== \"value\")\n return;\n const action = this._generateAction();\n if (!action)\n return;\n this._recorder.recordAction(action);\n this._recorder.setMode(\"recording\");\n (_a = this._recorder.overlay) == null ? void 0 : _a.flashToolSucceeded(\"assertingValue\");\n }\n};\nvar Overlay = class {\n // Track when snapshot toggle was activated\n //private _modularityToggled = false;\n constructor(recorder) {\n this._listeners = [];\n this._offsetX = 0;\n this._measure = { width: 0, height: 0 };\n this._snapshotToggleTime = null;\n this._recorder = recorder;\n const document2 = this._recorder.document;\n this._overlayElement = document2.createElement(\"x-pw-overlay\");\n const toolsListElement = document2.createElement(\"x-pw-tools-list\");\n this._overlayElement.appendChild(toolsListElement);\n this._dragHandle = document2.createElement(\"x-pw-tool-gripper\");\n this._dragHandle.appendChild(document2.createElement(\"x-div\"));\n toolsListElement.appendChild(this._dragHandle);\n this._recordToggle = this._recorder.document.createElement(\"x-pw-tool-item\");\n this._recordToggle.title = \"Record\";\n this._recordToggle.classList.add(\"record\");\n this._recordToggle.appendChild(this._recorder.document.createElement(\"x-div\"));\n toolsListElement.appendChild(this._recordToggle);\n this._pickLocatorToggle = this._recorder.document.createElement(\"x-pw-tool-item\");\n this._pickLocatorToggle.title = \"Pick locator\";\n this._pickLocatorToggle.classList.add(\"pick-locator\");\n this._pickLocatorToggle.appendChild(this._recorder.document.createElement(\"x-div\"));\n toolsListElement.appendChild(this._pickLocatorToggle);\n this._modularityToggle = this._recorder.document.createElement(\"x-pw-tool-item\");\n this._modularityToggle.title = \"Mark block\";\n this._modularityToggle.classList.add(\"modular\");\n this._modularityToggle.appendChild(this._recorder.document.createElement(\"x-div\"));\n toolsListElement.appendChild(this._modularityToggle);\n this._assertApiPayloadToggle = this._recorder.document.createElement(\"x-pw-tool-item\");\n this._assertApiPayloadToggle.title = \"Assert API Request\";\n this._assertApiPayloadToggle.classList.add(\"assert-api-payload\");\n this._assertApiPayloadToggle.appendChild(this._recorder.document.createElement(\"x-div\"));\n toolsListElement.appendChild(this._assertApiPayloadToggle);\n this._fileUploadToggle = this._recorder.document.createElement(\"x-pw-tool-item\");\n this._fileUploadToggle.title = \"Upload file\";\n this._fileUploadToggle.classList.add(\"file-upload\");\n this._fileUploadToggle.appendChild(this._recorder.document.createElement(\"x-div\"));\n toolsListElement.appendChild(this._fileUploadToggle);\n this._dragRecordToggle = this._recorder.document.createElement(\"x-pw-tool-item\");\n this._dragRecordToggle.title = \"Drag and drop\";\n this._dragRecordToggle.classList.add(\"drag-record\");\n this._dragRecordToggle.appendChild(this._recorder.document.createElement(\"x-div\"));\n toolsListElement.appendChild(this._dragRecordToggle);\n this._areaSelectToggle = this._recorder.document.createElement(\"x-pw-tool-item\");\n this._areaSelectToggle.title = \"Select area\";\n this._areaSelectToggle.classList.add(\"area-select\");\n this._areaSelectToggle.appendChild(this._recorder.document.createElement(\"x-div\"));\n toolsListElement.appendChild(this._areaSelectToggle);\n this._sketchToolToggle = this._recorder.document.createElement(\"x-pw-tool-item\");\n this._sketchToolToggle.title = \"Sketch Tool\";\n this._sketchToolToggle.classList.add(\"sketch-tool\");\n this._sketchToolToggle.appendChild(this._recorder.document.createElement(\"x-div\"));\n toolsListElement.appendChild(this._sketchToolToggle);\n this._gojsLinkToggle = this._recorder.document.createElement(\"x-pw-tool-item\");\n this._gojsLinkToggle.title = \"GoJS Link (click source node, then target node)\";\n this._gojsLinkToggle.classList.add(\"gojs-link\");\n this._gojsLinkToggle.appendChild(this._recorder.document.createElement(\"x-div\"));\n toolsListElement.appendChild(this._gojsLinkToggle);\n this._pointerEventsToggle = this._recorder.document.createElement(\"x-pw-tool-item\");\n this._pointerEventsToggle.title = \"Nested element selection\";\n this._pointerEventsToggle.classList.add(\"pointer-events\");\n this._pointerEventsToggle.appendChild(this._recorder.document.createElement(\"x-div\"));\n toolsListElement.appendChild(this._pointerEventsToggle);\n this._assertVisibilityToggle = this._recorder.document.createElement(\"x-pw-tool-item\");\n this._assertVisibilityToggle.title = \"Assert visibility\";\n this._assertVisibilityToggle.classList.add(\"visibility\");\n this._assertVisibilityToggle.appendChild(this._recorder.document.createElement(\"x-div\"));\n toolsListElement.appendChild(this._assertVisibilityToggle);\n this._assertTextToggle = this._recorder.document.createElement(\"x-pw-tool-item\");\n this._assertTextToggle.title = \"Assert text\";\n this._assertTextToggle.classList.add(\"text\");\n this._assertTextToggle.appendChild(this._recorder.document.createElement(\"x-div\"));\n toolsListElement.appendChild(this._assertTextToggle);\n this._assertValuesToggle = this._recorder.document.createElement(\"x-pw-tool-item\");\n this._assertValuesToggle.title = \"Assert value\";\n this._assertValuesToggle.classList.add(\"value\");\n this._assertValuesToggle.appendChild(this._recorder.document.createElement(\"x-div\"));\n toolsListElement.appendChild(this._assertValuesToggle);\n this._tableSnapshotToggle = this._recorder.document.createElement(\"x-pw-tool-item\");\n this._tableSnapshotToggle.title = \"Assert table cell\";\n this._tableSnapshotToggle.classList.add(\"table\");\n this._tableSnapshotToggle.appendChild(this._recorder.document.createElement(\"x-div\"));\n toolsListElement.appendChild(this._tableSnapshotToggle);\n this._assertVSnapshotToggle = this._recorder.document.createElement(\"x-pw-tool-item\");\n this._assertVSnapshotToggle.title = \"Snapshot: Double-toggle for page, Click for element, Drag for region\";\n this._assertVSnapshotToggle.classList.add(\"visual-snapshot\");\n this._assertVSnapshotToggle.appendChild(this._recorder.document.createElement(\"x-div\"));\n toolsListElement.appendChild(this._assertVSnapshotToggle);\n this._assertSnapshotToggle = this._recorder.document.createElement(\"x-pw-tool-item\");\n this._jsonMarkerButton = this._recorder.document.createElement(\"x-pw-tool-item\");\n this._updateVisualPosition();\n this._refreshListeners();\n }\n _refreshListeners() {\n removeEventListeners3(this._listeners);\n this._listeners = [\n addEventListener5(this._dragHandle, \"mousedown\", (event) => {\n this._dragState = { offsetX: this._offsetX, dragStart: { x: event.clientX, y: 0 } };\n }),\n addEventListener5(this._recordToggle, \"click\", () => {\n if (this._recordToggle.classList.contains(\"disabled\"))\n return;\n this._recorder.setMode(this._recorder.state.mode === \"none\" || this._recorder.state.mode === \"standby\" || this._recorder.state.mode === \"inspecting\" ? \"recording\" : \"standby\");\n }),\n addEventListener5(this._pickLocatorToggle, \"click\", () => {\n if (this._pickLocatorToggle.classList.contains(\"disabled\"))\n return;\n const newMode = {\n \"inspecting\": \"standby\",\n \"none\": \"inspecting\",\n \"standby\": \"inspecting\",\n \"recording\": \"recording-inspecting\",\n \"recording-inspecting\": \"recording\",\n \"assertingText\": \"recording-inspecting\",\n \"assertingVisibility\": \"recording-inspecting\",\n \"assertingValue\": \"recording-inspecting\",\n \"assertingSnapshot\": \"recording-inspecting\",\n \"assertingVSnapshot\": \"recording-inspecting\",\n \"assertingTableCell\": \"recording-inspecting\",\n \"recordingDrag\": \"recording\",\n \"recordingGoJSLink\": \"recording\",\n \"recordingArea\": \"recording\",\n \"recordingFileUpload\": \"recording\",\n \"recordingTableSnapshot\": \"recording\",\n \"recordingDomSnapshot\": \"recording\",\n \"recordingSketchTool\": \"recording\"\n };\n this._recorder.setMode(newMode[this._recorder.state.mode]);\n }),\n addEventListener5(this._assertVisibilityToggle, \"click\", () => {\n if (!this._assertVisibilityToggle.classList.contains(\"disabled\"))\n this._recorder.setMode(this._recorder.state.mode === \"assertingVisibility\" ? \"recording\" : \"assertingVisibility\");\n }),\n addEventListener5(this._assertTextToggle, \"click\", () => {\n if (!this._assertTextToggle.classList.contains(\"disabled\"))\n this._recorder.setMode(this._recorder.state.mode === \"assertingText\" ? \"recording\" : \"assertingText\");\n }),\n addEventListener5(this._assertValuesToggle, \"click\", () => {\n if (!this._assertValuesToggle.classList.contains(\"disabled\"))\n this._recorder.setMode(this._recorder.state.mode === \"assertingValue\" ? \"recording\" : \"assertingValue\");\n }),\n addEventListener5(this._assertSnapshotToggle, \"click\", () => {\n if (!this._assertSnapshotToggle.classList.contains(\"disabled\"))\n this._recorder.setMode(this._recorder.state.mode === \"assertingSnapshot\" ? \"recording\" : \"assertingSnapshot\");\n }),\n addEventListener5(this._assertVSnapshotToggle, \"click\", () => {\n if (this._assertVSnapshotToggle.classList.contains(\"disabled\"))\n return;\n const currentMode = this._recorder.state.mode;\n const isTogglingOff = currentMode === \"assertingVSnapshot\";\n if (isTogglingOff) {\n const now = Date.now();\n if (this._snapshotToggleTime && now - this._snapshotToggleTime < 1500) {\n VisualSnapshotTool.getNextCounter(this._recorder, \"page\").then((counter) => {\n const action = {\n name: \"visualSnapshot\",\n snapshotType: \"page\",\n filename: `page-${String(counter).padStart(3, \"0\")}.png`,\n fullPage: true,\n signals: [],\n timestamp: getTimestamp10(this._recorder)\n };\n this._recorder.recordAction(action);\n this.flashToolSucceeded(\"assertingVSnapshot\");\n });\n }\n this._snapshotToggleTime = null;\n this._recorder.setMode(\"recording\");\n } else {\n this._snapshotToggleTime = Date.now();\n this._recorder.setMode(\"assertingVSnapshot\");\n }\n }),\n addEventListener5(this._tableSnapshotToggle, \"click\", () => {\n if (!this._tableSnapshotToggle.classList.contains(\"disabled\"))\n this._recorder.setMode(this._recorder.state.mode === \"assertingTableCell\" ? \"recording\" : \"assertingTableCell\");\n }),\n addEventListener5(this._fileUploadToggle, \"click\", () => {\n if (!this._fileUploadToggle.classList.contains(\"disabled\")) {\n this._recorder.setMode(this._recorder.state.mode === \"recordingFileUpload\" ? \"recording\" : \"recordingFileUpload\");\n }\n }),\n addEventListener5(this._pointerEventsToggle, \"click\", () => {\n this._recorder.togglePointerEventsOverride();\n }),\n // addEventListener(this._jsonMarkerButton, 'click', () => {\n // if (this._jsonMarkerButton.classList.contains('disabled'))\n // return;\n // // console.log('fetch Custom JSON button clicked');\n // const sequence = Math.floor(Math.random() * 1000000);\n // // Record the marker action\n // const markerAction: actions.Action = {\n // name: 'marker',\n // timestamp: getTimestamp(this._recorder),\n // sequence: sequence,\n // signals: [],\n // };\n // this._recorder.recordAction(markerAction);\n // fetch('http://localhost:35142/skyramp/deploy/tracemarker')\n // .catch(error => {/* console.log(error) */});\n // }),\n addEventListener5(this._modularityToggle, \"click\", () => {\n if (this._modularityToggle.classList.contains(\"disabled\"))\n return;\n this._recorder.modularityToggled = !this._recorder.modularityToggled;\n let sectionBoundary = \"endBlock\";\n if (this._recorder.modularityToggled) {\n sectionBoundary = \"beginBlock\";\n }\n const modularAction = {\n name: sectionBoundary,\n timestamp: getTimestamp10(this._recorder),\n sequence: Math.floor(Math.random() * 1e6),\n signals: []\n };\n this._recorder.recordAction(modularAction);\n }),\n addEventListener5(this._assertApiPayloadToggle, \"click\", () => {\n if (this._assertApiPayloadToggle.classList.contains(\"disabled\"))\n return;\n const assertApiPayloadAction = {\n name: \"assertApiRequest\",\n timestamp: getTimestamp10(this._recorder),\n signals: []\n };\n this._recorder.recordAction(assertApiPayloadAction);\n this._assertApiPayloadToggle.classList.add(\"toggled\");\n setTimeout(() => this._assertApiPayloadToggle.classList.remove(\"toggled\"), 1500);\n }),\n addEventListener5(this._dragRecordToggle, \"click\", () => {\n if (this._dragRecordToggle.classList.contains(\"disabled\"))\n return;\n this._recorder.setMode(this._recorder.state.mode === \"recordingDrag\" ? \"recording\" : \"recordingDrag\");\n }),\n addEventListener5(this._areaSelectToggle, \"click\", () => {\n const mode = this._recorder.state.mode;\n if (mode === \"recordingArea\") {\n this._recorder.setMode(\"recording\");\n } else {\n this._recorder.setMode(\"recordingArea\");\n }\n }),\n addEventListener5(this._sketchToolToggle, \"click\", () => {\n if (!this._sketchToolToggle.classList.contains(\"disabled\")) {\n const mode = this._recorder.state.mode;\n if (mode === \"recordingSketchTool\") {\n this._recorder.setMode(\"recording\");\n } else {\n this._recorder.setMode(\"recordingSketchTool\");\n }\n }\n }),\n addEventListener5(this._gojsLinkToggle, \"click\", () => {\n if (!this._gojsLinkToggle.classList.contains(\"disabled\")) {\n const mode = this._recorder.state.mode;\n this._recorder.setMode(mode === \"recordingGoJSLink\" ? \"recording\" : \"recordingGoJSLink\");\n }\n })\n ];\n }\n install() {\n this._recorder.highlight.appendChild(this._overlayElement);\n this._refreshListeners();\n this._updateVisualPosition();\n const consumeEvent6 = (e) => {\n const target = e.target;\n if (target && (target === this._dragHandle || this._dragHandle.contains(target))) {\n if (e.type === \"mousedown\" || e.type === \"mousemove\" || e.type === \"mouseup\" || e.type === \"pointerdown\" || e.type === \"pointermove\" || e.type === \"pointerup\") {\n return;\n }\n }\n e.stopPropagation();\n e.preventDefault();\n };\n this._listeners.push(\n addEventListener5(this._overlayElement, \"mousedown\", consumeEvent6, false),\n addEventListener5(this._overlayElement, \"mouseup\", consumeEvent6, false),\n addEventListener5(this._overlayElement, \"mousemove\", consumeEvent6, false),\n addEventListener5(this._overlayElement, \"pointerdown\", consumeEvent6, false),\n addEventListener5(this._overlayElement, \"pointerup\", consumeEvent6, false),\n addEventListener5(this._overlayElement, \"pointermove\", consumeEvent6, false),\n addEventListener5(this._overlayElement, \"click\", consumeEvent6, false),\n addEventListener5(this._overlayElement, \"dblclick\", consumeEvent6, false),\n addEventListener5(this._overlayElement, \"contextmenu\", consumeEvent6, false),\n addEventListener5(this._overlayElement, \"focus\", consumeEvent6, false),\n addEventListener5(this._overlayElement, \"blur\", consumeEvent6, false)\n );\n }\n contains(element) {\n return this._recorder.injectedScript.utils.isInsideScope(this._overlayElement, element);\n }\n setUIState(state) {\n const isRecording = state.mode === \"recording\" || state.mode === \"assertingText\" || state.mode === \"assertingVisibility\" || state.mode === \"assertingValue\" || state.mode === \"assertingSnapshot\" || state.mode === \"assertingVSnapshot\" || state.mode === \"assertingTableCell\" || state.mode === \"recording-inspecting\" || state.mode === \"recordingDrag\" || state.mode === \"recordingGoJSLink\" || state.mode === \"recordingArea\" || state.mode === \"recordingFileUpload\" || state.mode === \"recordingSketchTool\";\n this._recordToggle.classList.toggle(\"toggled\", isRecording);\n this._recordToggle.title = isRecording ? \"Stop Recording\" : \"Start Recording\";\n this._pickLocatorToggle.classList.toggle(\"toggled\", state.mode === \"inspecting\" || state.mode === \"recording-inspecting\");\n this._pickLocatorToggle.classList.toggle(\"disabled\", state.mode === \"recordingArea\");\n this._assertVisibilityToggle.classList.toggle(\"toggled\", state.mode === \"assertingVisibility\");\n this._assertVisibilityToggle.classList.toggle(\"disabled\", state.mode === \"none\" || state.mode === \"standby\" || state.mode === \"inspecting\" || state.mode === \"recordingArea\");\n this._assertTextToggle.classList.toggle(\"toggled\", state.mode === \"assertingText\");\n this._assertTextToggle.classList.toggle(\"disabled\", state.mode === \"none\" || state.mode === \"standby\" || state.mode === \"inspecting\" || state.mode === \"recordingArea\");\n this._assertValuesToggle.classList.toggle(\"toggled\", state.mode === \"assertingValue\");\n this._assertValuesToggle.classList.toggle(\"disabled\", state.mode === \"none\" || state.mode === \"standby\" || state.mode === \"inspecting\" || state.mode === \"recordingArea\");\n this._assertSnapshotToggle.classList.toggle(\"toggled\", state.mode === \"assertingSnapshot\");\n this._assertSnapshotToggle.classList.toggle(\"disabled\", state.mode === \"none\" || state.mode === \"standby\" || state.mode === \"inspecting\" || state.mode === \"recordingArea\");\n this._assertVSnapshotToggle.classList.toggle(\"toggled\", state.mode === \"assertingVSnapshot\");\n this._assertVSnapshotToggle.classList.toggle(\"disabled\", state.mode === \"none\" || state.mode === \"standby\" || state.mode === \"inspecting\" || state.mode === \"recordingArea\");\n this._tableSnapshotToggle.classList.toggle(\"toggled\", state.mode === \"assertingTableCell\");\n this._tableSnapshotToggle.classList.toggle(\"disabled\", state.mode === \"none\" || state.mode === \"standby\" || state.mode === \"inspecting\" || state.mode === \"recordingArea\");\n this._fileUploadToggle.classList.toggle(\"toggled\", state.mode === \"recordingFileUpload\");\n this._fileUploadToggle.classList.toggle(\"disabled\", state.mode === \"none\" || state.mode === \"standby\" || state.mode === \"inspecting\" || state.mode === \"recordingArea\");\n this._modularityToggle.classList.toggle(\"disabled\", state.mode === \"none\" || state.mode === \"standby\" || state.mode === \"inspecting\" || state.mode === \"recordingArea\");\n this._assertApiPayloadToggle.classList.toggle(\"disabled\", state.mode === \"none\" || state.mode === \"standby\" || state.mode === \"inspecting\" || state.mode === \"recordingArea\");\n this._dragRecordToggle.classList.toggle(\"toggled\", state.mode === \"recordingDrag\");\n this._dragRecordToggle.classList.toggle(\"disabled\", state.mode === \"none\" || state.mode === \"standby\" || state.mode === \"inspecting\" || state.mode === \"recordingArea\");\n this._areaSelectToggle.classList.toggle(\"toggled\", state.mode === \"recordingArea\");\n this._areaSelectToggle.classList.toggle(\"disabled\", state.mode === \"none\");\n this._sketchToolToggle.classList.toggle(\"toggled\", state.mode === \"recordingSketchTool\");\n this._sketchToolToggle.classList.toggle(\"disabled\", state.mode === \"none\" || state.mode === \"standby\" || state.mode === \"inspecting\" || state.mode === \"recordingArea\");\n this._gojsLinkToggle.classList.toggle(\"toggled\", state.mode === \"recordingGoJSLink\");\n this._gojsLinkToggle.classList.toggle(\"disabled\", state.mode === \"none\" || state.mode === \"standby\" || state.mode === \"inspecting\" || state.mode === \"recordingArea\");\n this.updateToolbar();\n if (this._offsetX !== state.overlay.offsetX) {\n this._offsetX = state.overlay.offsetX;\n this._updateVisualPosition();\n }\n if (state.mode === \"none\")\n this._hideOverlay();\n else\n this._showOverlay();\n }\n updateToolbar() {\n this._pointerEventsToggle.classList.toggle(\"toggled\", this._recorder.pointerEventsOverrideEnabled);\n }\n flashToolSucceeded(tool) {\n let element;\n if (tool === \"assertingVisibility\")\n element = this._assertVisibilityToggle;\n else if (tool === \"assertingSnapshot\")\n element = this._assertSnapshotToggle;\n else if (tool === \"assertingVSnapshot\")\n element = this._assertVSnapshotToggle;\n else if (tool === \"assertingTableCell\")\n element = this._tableSnapshotToggle;\n else if (tool === \"fileUpload\")\n element = this._fileUploadToggle;\n else if (tool === \"recordingArea\")\n element = this._areaSelectToggle;\n else if (tool === \"recordingTableSnapshot\")\n element = this._tableSnapshotToggle;\n else if (tool === \"recordingSketchTool\")\n element = this._sketchToolToggle;\n else\n element = this._assertValuesToggle;\n element.classList.add(\"succeeded\");\n this._recorder.injectedScript.utils.builtins.setTimeout(() => element.classList.remove(\"succeeded\"), 2e3);\n }\n _hideOverlay() {\n this._overlayElement.setAttribute(\"hidden\", \"true\");\n }\n _showOverlay() {\n if (!this._overlayElement.hasAttribute(\"hidden\"))\n return;\n this._overlayElement.removeAttribute(\"hidden\");\n this._updateVisualPosition();\n }\n _updateVisualPosition() {\n this._measure = this._overlayElement.getBoundingClientRect();\n this._overlayElement.style.left = (this._recorder.injectedScript.window.innerWidth - this._measure.width) / 2 + this._offsetX + \"px\";\n }\n onMouseMove(event) {\n if (!event.buttons) {\n this._dragState = void 0;\n return false;\n }\n if (this._dragState) {\n this._offsetX = this._dragState.offsetX + event.clientX - this._dragState.dragStart.x;\n const halfGapSize = (this._recorder.injectedScript.window.innerWidth - this._measure.width) / 2 - 10;\n this._offsetX = Math.max(-halfGapSize, Math.min(halfGapSize, this._offsetX));\n this._updateVisualPosition();\n this._recorder.setOverlayState({ offsetX: this._offsetX });\n consumeEvent5(event);\n return true;\n }\n return false;\n }\n onMouseUp(event) {\n if (this._dragState) {\n consumeEvent5(event);\n return true;\n }\n return false;\n }\n onClick(event) {\n if (this._dragState) {\n this._dragState = void 0;\n consumeEvent5(event);\n return true;\n }\n return false;\n }\n onDblClick(event) {\n return false;\n }\n // method to update modularity toggle state in the UI\n updateModularityToggleState(toggled) {\n this._modularityToggle.classList.toggle(\"toggled\", toggled);\n }\n};\nvar _Recorder = class _Recorder {\n constructor(injectedScript, options) {\n this._listeners = [];\n this._lastHighlightedSelector = void 0;\n this._lastHighlightedAriaTemplateJSON = \"undefined\";\n this.state = {\n mode: \"none\",\n testIdAttributeName: \"data-testid\",\n language: \"javascript\",\n overlay: { offsetX: 0 },\n modularityToggled: false\n };\n this._delegate = {};\n this._modularityToggled = false;\n // SKYR-3747: short-lived buffer of recently hovered, \"menu-trigger-like\" elements.\n // Used to synthesize a hover action before a click whose preconditionSelector\n // points to a freshly-revealed flyout container (Cisco XDR Client Mgmt → Profiles).\n this._recentHoverTrail = [];\n // SKYR-3781: previous recorded user action (see _maybeEmitFlyoutHoverBeforeClick).\n this._previousUserActionName = void 0;\n var _a, _b;\n this.document = injectedScript.document;\n this.injectedScript = injectedScript;\n this.highlight = injectedScript.createHighlight();\n this._nestedElementHandler = new NestedElementHandler(this.document);\n this._modalHandler = new ModalHandler(this.document);\n this._modalHandler.setOnModalOpen(({ selector }) => {\n this.recordAction({\n name: \"modalOpen\",\n selector,\n signals: [],\n timestamp: getTimestamp10(this)\n });\n });\n this._modalHandler.setOnModalClose(({ selector }) => {\n this.recordAction({\n name: \"modalClose\",\n selector,\n signals: [],\n timestamp: getTimestamp10(this)\n });\n });\n this._iframeHandler = new IframeHandler(this.document);\n this._iframeHandler.setOnIframeLoad(({ selector }) => {\n this.recordAction({\n name: \"iframeLoad\",\n selector,\n signals: [],\n timestamp: getTimestamp10(this)\n });\n });\n this._tools = {\n \"none\": new NoneTool(),\n \"standby\": new NoneTool(),\n \"inspecting\": new InspectTool(this, false),\n \"recording\": (options == null ? void 0 : options.recorderMode) === \"api\" ? new JsonRecordActionTool(this) : new RecordActionTool(this),\n \"recording-inspecting\": new InspectTool(this, false),\n \"assertingText\": new TextAssertionTool(this, \"text\"),\n \"assertingVisibility\": new InspectTool(this, true),\n \"assertingValue\": new TextAssertionTool(this, \"value\"),\n \"assertingSnapshot\": new TextAssertionTool(this, \"snapshot\"),\n \"assertingVSnapshot\": new VisualSnapshotTool(this),\n \"assertingTableCell\": new TableAssertTool(this),\n \"recordingDrag\": new DragDropTool(this),\n \"recordingGoJSLink\": new GoJSLinkTool(this),\n \"recordingArea\": new AreaSelectionTool(this),\n \"recordingFileUpload\": new FileUploadTool(this),\n \"recordingTableSnapshot\": new TableSnapshotTool(this),\n \"recordingDomSnapshot\": new DomSnapshotTool(this),\n \"recordingSketchTool\": new SketchTool(this),\n \"replaying\": new NoneTool()\n };\n this._currentTool = this._tools.none;\n (_b = (_a = this._currentTool).install) == null ? void 0 : _b.call(_a);\n if (injectedScript.window.top === injectedScript.window && (options == null ? void 0 : options.recorderMode) !== \"api\") {\n this.overlay = new Overlay(this);\n this.overlay.setUIState(this.state);\n }\n this._stylesheet = new injectedScript.window.CSSStyleSheet();\n this._stylesheet.replaceSync(`\n body[data-pw-cursor=pointer] *, body[data-pw-cursor=pointer] *::after { cursor: pointer !important; }\n body[data-pw-cursor=text] *, body[data-pw-cursor=text] *::after { cursor: text !important; }\n body[data-pw-cursor=crosshair] *, body[data-pw-cursor=crosshair] *::after { cursor: crosshair !important; }\n body[data-pw-cursor=grab] *, body[data-pw-cursor=grab] *::after { cursor: grab !important; }\n `);\n this.installListeners();\n this._installFileUploadHooks();\n injectedScript.utils.cacheNormalizedWhitespaces();\n if (injectedScript.isUnderTest) {\n console.error(\"Recorder script ready for test\");\n injectedScript.window.__pw_recorderToggleNestedElements = () => {\n this.togglePointerEventsOverride();\n };\n }\n injectedScript.consoleApi.install();\n }\n get modularityToggled() {\n return this._modularityToggled;\n }\n set modularityToggled(value) {\n this._modularityToggled = value;\n try {\n if (typeof window.__pw_recorderSetModularityToggled === \"function\") {\n window.__pw_recorderSetModularityToggled(value);\n } else {\n console.warn(\"Modularity toggle binding not available yet, state may be out of sync\");\n }\n } catch (e) {\n console.error(\"Failed to set modularity toggle on server:\", e);\n }\n }\n get pointerEventsOverrideEnabled() {\n return this._nestedElementHandler.enabled;\n }\n togglePointerEventsOverride() {\n var _a;\n this._nestedElementHandler.toggle();\n (_a = this.overlay) == null ? void 0 : _a.updateToolbar();\n }\n installListeners() {\n var _a, _b;\n removeEventListeners3(this._listeners);\n this._listeners = [\n addEventListener5(this.document, \"click\", (event) => this._onClick(event), true),\n addEventListener5(this.document, \"auxclick\", (event) => this._onClick(event), true),\n addEventListener5(this.document, \"dblclick\", (event) => this._onDblClick(event), true),\n addEventListener5(this.document, \"contextmenu\", (event) => this._onContextMenu(event), true),\n addEventListener5(this.document, \"dragstart\", (event) => this._onDragStart(event), true),\n addEventListener5(this.document, \"input\", (event) => this._onInput(event), true),\n addEventListener5(this.document, \"keydown\", (event) => this._onKeyDown(event), true),\n addEventListener5(this.document, \"keyup\", (event) => this._onKeyUp(event), true),\n addEventListener5(this.document, \"pointerdown\", (event) => this._onPointerDown(event), true),\n addEventListener5(this.document, \"pointermove\", (event) => this._onPointerMove(event), true),\n addEventListener5(this.document, \"pointerup\", (event) => this._onPointerUp(event), true),\n addEventListener5(this.document, \"mousedown\", (event) => this._onMouseDown(event), true),\n addEventListener5(this.document, \"mouseup\", (event) => this._onMouseUp(event), true),\n addEventListener5(this.document, \"mousemove\", (event) => this._onMouseMove(event), true),\n addEventListener5(this.document, \"mouseleave\", (event) => this._onMouseLeave(event), true),\n addEventListener5(this.document, \"mouseenter\", (event) => this._onMouseEnter(event), true),\n addEventListener5(this.document, \"focus\", (event) => this._onFocus(event), true),\n addEventListener5(this.document, \"scroll\", (event) => this._onScroll(event), true)\n ];\n this.highlight.install();\n let recreationInterval;\n const recreate = () => {\n this.highlight.install();\n if (this.overlay) {\n const overlayElement = this.overlay._overlayElement;\n const glassPaneElement = this.highlight._glassPaneElement;\n const glassPaneConnected = glassPaneElement && glassPaneElement.isConnected;\n const overlayDisconnected = overlayElement && !overlayElement.isConnected;\n if (glassPaneConnected && overlayDisconnected) {\n this.overlay.install();\n }\n }\n recreationInterval = this.injectedScript.utils.builtins.setTimeout(recreate, 500);\n };\n recreationInterval = this.injectedScript.utils.builtins.setTimeout(recreate, 500);\n this._listeners.push(() => this.injectedScript.utils.builtins.clearTimeout(recreationInterval));\n this.highlight.appendChild(createSvgElement(this.document, clipPaths_default));\n if (this.overlay) {\n const glassPaneElement = this.highlight._glassPaneElement;\n if (glassPaneElement && glassPaneElement.isConnected) {\n this.overlay.install();\n }\n }\n (_b = (_a = this._currentTool) == null ? void 0 : _a.install) == null ? void 0 : _b.call(_a);\n this.document.adoptedStyleSheets.push(this._stylesheet);\n }\n _installFileUploadHooks() {\n installFileUploadHooks(this, this._listeners);\n }\n _switchCurrentTool() {\n var _a, _b, _c, _d, _e, _f, _g, _h;\n const newTool = this._tools[this.state.mode];\n if (newTool === this._currentTool)\n return;\n (_b = (_a = this._currentTool).uninstall) == null ? void 0 : _b.call(_a);\n this.clearHighlight();\n this._currentTool = newTool;\n (_d = (_c = this._currentTool).install) == null ? void 0 : _d.call(_c);\n if (this.state.mode === \"recording\") {\n const activeEl = deepActiveElement(this.document);\n if (activeEl && activeEl !== this.document.body && activeEl !== this.document.documentElement)\n (_f = (_e = this._currentTool).onFocus) == null ? void 0 : _f.call(_e, new FocusEvent(\"focus\"));\n }\n const cursor = (_g = newTool.cursor) == null ? void 0 : _g.call(newTool);\n if (cursor)\n (_h = this.injectedScript.document.body) == null ? void 0 : _h.setAttribute(\"data-pw-cursor\", cursor);\n }\n setUIState(state, delegate) {\n var _a, _b;\n this._delegate = delegate;\n if (state.actionPoint && this.state.actionPoint && state.actionPoint.x === this.state.actionPoint.x && state.actionPoint.y === this.state.actionPoint.y) {\n } else if (!state.actionPoint && !this.state.actionPoint) {\n } else {\n if (state.actionPoint)\n this.highlight.showActionPoint(state.actionPoint.x, state.actionPoint.y);\n else\n this.highlight.hideActionPoint();\n }\n if (state.modularityToggled !== this._modularityToggled) {\n this._modularityToggled = state.modularityToggled;\n (_a = this.overlay) == null ? void 0 : _a.updateModularityToggleState(this._modularityToggled);\n }\n this.state = state;\n this.highlight.setLanguage(state.language);\n this._switchCurrentTool();\n (_b = this.overlay) == null ? void 0 : _b.setUIState(state);\n if (state.mode === \"recording\") {\n this._modalHandler.enable();\n this._iframeHandler.enable();\n } else {\n this._modalHandler.disable();\n this._iframeHandler.disable();\n }\n let highlight = \"noop\";\n if (state.actionSelector !== this._lastHighlightedSelector) {\n const entries = state.actionSelector ? entriesForSelectorHighlight(this.injectedScript, state.language, state.actionSelector, this.document) : null;\n highlight = (entries == null ? void 0 : entries.length) ? entries : \"clear\";\n this._lastHighlightedSelector = (entries == null ? void 0 : entries.length) ? state.actionSelector : void 0;\n }\n const ariaTemplateJSON = JSON.stringify(state.ariaTemplate);\n if (this._lastHighlightedAriaTemplateJSON !== ariaTemplateJSON) {\n const elements = state.ariaTemplate ? this.injectedScript.getAllElementsMatchingExpectAriaTemplate(this.document, state.ariaTemplate) : [];\n if (elements.length) {\n const color = elements.length > 1 ? HighlightColors2.multiple : HighlightColors2.single;\n highlight = elements.map((element) => ({ element, color }));\n this._lastHighlightedAriaTemplateJSON = ariaTemplateJSON;\n } else {\n if (!this._lastHighlightedSelector)\n highlight = \"clear\";\n this._lastHighlightedAriaTemplateJSON = \"undefined\";\n }\n }\n if (highlight === \"clear\")\n this.highlight.clearHighlight();\n else if (highlight !== \"noop\")\n this.highlight.updateHighlight(highlight);\n }\n clearHighlight() {\n this.updateHighlight(null, false);\n }\n _onClick(event) {\n var _a, _b, _c;\n if (!event.isTrusted)\n return;\n if ((_a = this.overlay) == null ? void 0 : _a.onClick(event))\n return;\n if (this._ignoreOverlayEvent(event))\n return;\n (_c = (_b = this._currentTool).onClick) == null ? void 0 : _c.call(_b, event);\n }\n _onDblClick(event) {\n var _a, _b, _c;\n if (!event.isTrusted)\n return;\n if ((_a = this.overlay) == null ? void 0 : _a.onDblClick(event))\n return;\n if (this._ignoreOverlayEvent(event))\n return;\n (_c = (_b = this._currentTool).onDblClick) == null ? void 0 : _c.call(_b, event);\n }\n _onContextMenu(event) {\n var _a, _b;\n if (!event.isTrusted)\n return;\n (_b = (_a = this._currentTool).onContextMenu) == null ? void 0 : _b.call(_a, event);\n }\n _onDragStart(event) {\n var _a, _b;\n if (!event.isTrusted)\n return;\n if (this._ignoreOverlayEvent(event))\n return;\n (_b = (_a = this._currentTool).onDragStart) == null ? void 0 : _b.call(_a, event);\n }\n _onPointerDown(event) {\n var _a, _b;\n if (!event.isTrusted)\n return;\n if (this._ignoreOverlayEvent(event))\n return;\n (_b = (_a = this._currentTool).onPointerDown) == null ? void 0 : _b.call(_a, event);\n }\n _onPointerUp(event) {\n var _a, _b;\n if (!event.isTrusted)\n return;\n if (this._ignoreOverlayEvent(event))\n return;\n (_b = (_a = this._currentTool).onPointerUp) == null ? void 0 : _b.call(_a, event);\n }\n _onPointerMove(event) {\n var _a, _b;\n if (!event.isTrusted)\n return;\n if (this._ignoreOverlayEvent(event))\n return;\n (_b = (_a = this._currentTool).onPointerMove) == null ? void 0 : _b.call(_a, event);\n }\n _onMouseDown(event) {\n var _a, _b;\n if (!event.isTrusted)\n return;\n if (this._ignoreOverlayEvent(event))\n return;\n (_b = (_a = this._currentTool).onMouseDown) == null ? void 0 : _b.call(_a, event);\n }\n _onMouseUp(event) {\n var _a, _b, _c;\n if (!event.isTrusted)\n return;\n if ((_a = this.overlay) == null ? void 0 : _a.onMouseUp(event))\n return;\n if (this._ignoreOverlayEvent(event))\n return;\n (_c = (_b = this._currentTool).onMouseUp) == null ? void 0 : _c.call(_b, event);\n }\n _onMouseMove(event) {\n var _a, _b, _c;\n if (!event.isTrusted)\n return;\n if ((_a = this.overlay) == null ? void 0 : _a.onMouseMove(event))\n return;\n if (this._ignoreOverlayEvent(event))\n return;\n this._trackHoverTrail(event);\n (_c = (_b = this._currentTool).onMouseMove) == null ? void 0 : _c.call(_b, event);\n }\n _onMouseEnter(event) {\n var _a, _b;\n if (!event.isTrusted)\n return;\n if (this._ignoreOverlayEvent(event))\n return;\n (_b = (_a = this._currentTool).onMouseEnter) == null ? void 0 : _b.call(_a, event);\n }\n // SKYR-3747: append the closest menu-trigger ancestor of the pointer target\n // to the hover trail. We only track menu-trigger-like elements, dedupe\n // against the previous entry, and bound the buffer by size and age so\n // lookups stay cheap and stale entries don't survive across user flows.\n _trackHoverTrail(event) {\n if (this.state.mode !== \"recording\")\n return;\n const initial = this.deepEventTarget(event);\n if (!initial)\n return;\n const trigger = closestMenuTrigger(initial, this.document);\n if (!trigger)\n return;\n const now = performance.now();\n const last = this._recentHoverTrail[this._recentHoverTrail.length - 1];\n if (last && last.element === trigger)\n return;\n this._recentHoverTrail.push({ element: trigger, timestamp: now });\n const cutoff = now - _Recorder._HOVER_TRAIL_LOOKBACK_MS;\n while (this._recentHoverTrail.length > 0 && this._recentHoverTrail[0].timestamp < cutoff)\n this._recentHoverTrail.shift();\n if (this._recentHoverTrail.length > _Recorder._HOVER_TRAIL_MAX)\n this._recentHoverTrail.splice(0, this._recentHoverTrail.length - _Recorder._HOVER_TRAIL_MAX);\n }\n _onMouseLeave(event) {\n var _a, _b;\n if (!event.isTrusted)\n return;\n if (this._ignoreOverlayEvent(event))\n return;\n (_b = (_a = this._currentTool).onMouseLeave) == null ? void 0 : _b.call(_a, event);\n }\n _onFocus(event) {\n var _a, _b;\n if (this._ignoreOverlayEvent(event))\n return;\n (_b = (_a = this._currentTool).onFocus) == null ? void 0 : _b.call(_a, event);\n }\n _onScroll(event) {\n var _a, _b;\n if (!event.isTrusted)\n return;\n this._lastHighlightedSelector = void 0;\n this._lastHighlightedAriaTemplateJSON = \"undefined\";\n this.highlight.hideActionPoint();\n (_b = (_a = this._currentTool).onScroll) == null ? void 0 : _b.call(_a, event);\n }\n _onInput(event) {\n var _a, _b;\n if (!event.isTrusted)\n return;\n if (this._ignoreOverlayEvent(event))\n return;\n (_b = (_a = this._currentTool).onInput) == null ? void 0 : _b.call(_a, event);\n }\n _onKeyDown(event) {\n var _a, _b;\n if (!event.isTrusted)\n return;\n if (this._ignoreOverlayEvent(event))\n return;\n (_b = (_a = this._currentTool).onKeyDown) == null ? void 0 : _b.call(_a, event);\n }\n _onKeyUp(event) {\n var _a, _b;\n if (!event.isTrusted)\n return;\n if (this._ignoreOverlayEvent(event))\n return;\n (_b = (_a = this._currentTool).onKeyUp) == null ? void 0 : _b.call(_a, event);\n }\n updateHighlight(model, userGesture) {\n this._lastHighlightedSelector = void 0;\n this._lastHighlightedAriaTemplateJSON = \"undefined\";\n this._updateHighlight(model, userGesture);\n }\n _updateHighlight(model, userGesture) {\n var _a, _b;\n let tooltipText = model == null ? void 0 : model.tooltipText;\n if (tooltipText === void 0 && (model == null ? void 0 : model.selector))\n tooltipText = this.injectedScript.utils.asLocator(this.state.language, model.selector);\n if (model)\n this.highlight.updateHighlight(model.elements.map((element) => ({ element, color: model.color, tooltipText })));\n else\n this.highlight.clearHighlight();\n if (userGesture)\n (_b = (_a = this._delegate).highlightUpdated) == null ? void 0 : _b.call(_a);\n }\n _ignoreOverlayEvent(event) {\n return event.composedPath().some((e) => {\n const nodeName = e.nodeName || \"\";\n return nodeName.toLowerCase() === \"x-pw-glass\";\n });\n }\n deepEventTarget(event) {\n var _a;\n for (const element of event.composedPath()) {\n if (!((_a = this.overlay) == null ? void 0 : _a.contains(element)))\n return element;\n }\n return event.composedPath()[0];\n }\n setMode(mode) {\n var _a, _b;\n void ((_b = (_a = this._delegate).setMode) == null ? void 0 : _b.call(_a, mode));\n }\n _captureAutoExpectSnapshot() {\n const documentElement = this.injectedScript.document.documentElement;\n return documentElement ? this.injectedScript.utils.generateAriaTree(documentElement, { mode: \"autoexpect\" }) : void 0;\n }\n async performAction(action) {\n var _a, _b;\n this._decorateUserAction(action);\n await ((_b = (_a = this._delegate).performAction) == null ? void 0 : _b.call(_a, action).catch(() => {\n }));\n }\n // Updates the aria-snapshot baseline, computes preconditionSelector for\n // non-assert actions, and (for clicks) synthesizes a hover action when the\n // click target lives inside a freshly-revealed flyout container (SKYR-3747).\n // The synthetic hover is fire-and-forget so the caller can dispatch the\n // primary action immediately afterward — important for <a href> clicks that\n // tear down the page before any awaited recordAction round-trip resolves.\n _decorateUserAction(action) {\n var _a;\n const previousSnapshot = this._lastActionAutoexpectSnapshot;\n this._lastActionAutoexpectSnapshot = this._captureAutoExpectSnapshot();\n if (isAssertAction(action) || !this._lastActionAutoexpectSnapshot)\n return;\n const revealedElement = this.injectedScript.utils.findNewElement(previousSnapshot == null ? void 0 : previousSnapshot.root, (_a = this._lastActionAutoexpectSnapshot) == null ? void 0 : _a.root);\n if (!(\"preconditionSelector\" in action) || action.preconditionSelector === void 0) {\n const withSelector = action;\n withSelector.preconditionSelector = revealedElement ? this.injectedScript.generateSelector(revealedElement, { testIdAttributeName: this.state.testIdAttributeName }).selector : void 0;\n if (\"selector\" in action && withSelector.preconditionSelector === withSelector.selector)\n withSelector.preconditionSelector = void 0;\n }\n if (action.name === \"click\" && action.preconditionSelector && revealedElement)\n this._maybeEmitFlyoutHoverBeforeClick(action, revealedElement);\n if (action.name === \"click\")\n this._maybeEmitRowRevealHoverBeforeClick(action);\n this._previousUserActionName = action.name;\n }\n // SKYR-3744 Gap 5 / SKYR-3781: when a click lands on a per-row action control\n // (button/menuitem) inside a row-like container, emit a hover on the row\n // first so replay reproduces the CSS :hover that revealed the control (Box\n // \"More Options <name>\"). Lives here (not in a tool) so it runs in both the\n // default and api recorder modes, and is decided from the DOM at click time\n // — not from a mousemove snapshot — so it fires even when the cursor lands on\n // the control after a navigation with no intervening mousemove into the row.\n // That determinism is what makes codegen output replay-clean without edits.\n _maybeEmitRowRevealHoverBeforeClick(clickAction) {\n var _a, _b;\n let clickTarget = null;\n try {\n const parsed = this.injectedScript.parseSelector(clickAction.selector);\n clickTarget = (_a = this.injectedScript.querySelectorAll(parsed, this.document)[0]) != null ? _a : null;\n } catch {\n return;\n }\n if (!clickTarget)\n return;\n const container = clickTarget.closest(_Recorder._REVEAL_CONTAINER_SELECTOR);\n if (!container)\n return;\n const control = clickTarget.closest('button, [role=\"button\"], [role=\"menuitem\"]');\n if (!control || !container.contains(control))\n return;\n const hoverTargetElement = (_b = this._findRevealHoverTarget(container)) != null ? _b : container;\n let selector;\n try {\n selector = this.injectedScript.generateSelector(hoverTargetElement, { testIdAttributeName: this.state.testIdAttributeName }).selector;\n } catch {\n return;\n }\n if (!selector)\n return;\n const clickTs = parseInt(clickAction.timestamp, 10);\n const hoverAction = {\n name: \"hover\",\n selector,\n signals: [],\n timestamp: Number.isFinite(clickTs) ? (clickTs - 1).toString() : clickAction.timestamp\n };\n if (this._delegate.recordAction)\n void this._delegate.recordAction(hoverAction).catch(() => {\n });\n }\n // SKYR-3744: most stable distinguishing descendant of a row-like container —\n // a link/heading with a short, view-agnostic accessible name (the item's own\n // name like \"test123\"), falling back to null. See _maybeEmitRowRevealHoverBeforeClick.\n _findRevealHoverTarget(container) {\n const candidates = container.querySelectorAll('a[href], [role=\"link\"], h1, h2, h3, h4, h5, h6');\n for (const el of Array.from(candidates)) {\n const text = (el.innerText || el.textContent || \"\").trim();\n if (!text || text.length > 80)\n continue;\n return el;\n }\n return null;\n }\n _maybeEmitFlyoutHoverBeforeClick(clickAction, revealedElement) {\n var _a;\n if (this._recentHoverTrail.length === 0)\n return;\n if (this._previousUserActionName === \"click\")\n return;\n let clickTarget = null;\n try {\n const parsed = this.injectedScript.parseSelector(clickAction.selector);\n const matches = this.injectedScript.querySelectorAll(parsed, this.document);\n clickTarget = (_a = matches[0]) != null ? _a : null;\n } catch {\n return;\n }\n if (!clickTarget || !revealedElement.contains(clickTarget))\n return;\n for (let i = this._recentHoverTrail.length - 1; i >= 0; i--) {\n const candidate = this._recentHoverTrail[i].element;\n if (!candidate.isConnected)\n continue;\n if (candidate === clickTarget)\n continue;\n if (clickTarget.contains(candidate) || candidate.contains(clickTarget))\n continue;\n if (revealedElement.contains(candidate) || candidate.contains(revealedElement))\n continue;\n const generated = this.injectedScript.generateSelector(candidate, { testIdAttributeName: this.state.testIdAttributeName });\n if (!generated.selector)\n continue;\n const clickTs = parseInt(clickAction.timestamp, 10);\n const hoverAction = {\n name: \"hover\",\n selector: generated.selector,\n signals: [],\n timestamp: Number.isFinite(clickTs) ? (clickTs - 1).toString() : clickAction.timestamp\n };\n if (this._delegate.recordAction)\n void this._delegate.recordAction(hoverAction).catch(() => {\n });\n this._recentHoverTrail = [];\n return;\n }\n }\n recordAction(action) {\n this._decorateUserAction(action);\n if (this._delegate.recordAction) {\n void this._delegate.recordAction(action);\n } else {\n console.warn(\"[Recorder] No delegate.recordAction available!\");\n }\n }\n setOverlayState(state) {\n var _a, _b;\n void ((_b = (_a = this._delegate).setOverlayState) == null ? void 0 : _b.call(_a, state));\n }\n elementPicked(selector, model) {\n var _a, _b;\n const ariaSnapshot = this.injectedScript.ariaSnapshot(model.elements[0], { mode: \"expect\" });\n void ((_b = (_a = this._delegate).elementPicked) == null ? void 0 : _b.call(_a, { selector, ariaSnapshot }));\n }\n};\n_Recorder._HOVER_TRAIL_MAX = 20;\n_Recorder._HOVER_TRAIL_LOOKBACK_MS = 1e4;\n// SKYR-3744 Gap 5: row-like containers whose per-row action controls are\n// revealed only on hover (Box files grid, Linear/GitHub row actions, …).\n_Recorder._REVEAL_CONTAINER_SELECTOR = '[role=\"row\"], [data-testid=\"grid-view-item\"], [data-testid$=\"-item\"], [data-testid$=\"-row\"], [draggable=\"true\"], tr';\nvar Recorder = _Recorder;\nvar Dialog = class {\n constructor(recorder) {\n this._dialogElement = null;\n this._recorder = recorder;\n }\n isShowing() {\n return !!this._dialogElement;\n }\n show(options) {\n const acceptButton = this._recorder.document.createElement(\"x-pw-tool-item\");\n acceptButton.title = \"Accept\";\n acceptButton.classList.add(\"accept\");\n acceptButton.appendChild(this._recorder.document.createElement(\"x-div\"));\n acceptButton.addEventListener(\"click\", () => {\n var _a;\n return (_a = options.onCommit) == null ? void 0 : _a.call(options);\n });\n const cancelButton = this._recorder.document.createElement(\"x-pw-tool-item\");\n cancelButton.title = \"Close\";\n cancelButton.classList.add(\"cancel\");\n cancelButton.appendChild(this._recorder.document.createElement(\"x-div\"));\n cancelButton.addEventListener(\"click\", () => {\n var _a;\n this.close();\n (_a = options.onCancel) == null ? void 0 : _a.call(options);\n });\n this._dialogElement = this._recorder.document.createElement(\"x-pw-dialog\");\n if (options.autosize)\n this._dialogElement.classList.add(\"autosize\");\n this._keyboardListener = (event) => {\n var _a;\n if (event.key === \"Escape\") {\n this.close();\n (_a = options.onCancel) == null ? void 0 : _a.call(options);\n return;\n }\n if (options.onCommit && event.key === \"Enter\" && (event.ctrlKey || event.metaKey)) {\n if (this._dialogElement)\n options.onCommit();\n return;\n }\n };\n this._onGlassPaneClickHandler = (event) => {\n var _a;\n if (this._dialogElement && event.target instanceof Node && this._dialogElement.contains(event.target))\n return;\n this.close();\n (_a = options.onCancel) == null ? void 0 : _a.call(options);\n };\n this._dialogElement.addEventListener(\"click\", (event) => event.stopPropagation());\n const toolbarElement = this._recorder.document.createElement(\"x-pw-tools-list\");\n const labelElement = this._recorder.document.createElement(\"label\");\n labelElement.textContent = options.label;\n toolbarElement.appendChild(labelElement);\n toolbarElement.appendChild(this._recorder.document.createElement(\"x-spacer\"));\n if (options.onCommit)\n toolbarElement.appendChild(acceptButton);\n toolbarElement.appendChild(cancelButton);\n this._dialogElement.appendChild(toolbarElement);\n const bodyElement = this._recorder.document.createElement(\"x-pw-dialog-body\");\n bodyElement.appendChild(options.body);\n this._dialogElement.appendChild(bodyElement);\n toolbarElement.style.cursor = \"move\";\n let dragStartX = 0, dragStartY = 0, dragStartTop = 0, dragStartLeft = 0;\n const onDragMove = (e) => {\n if (!this._dialogElement) return;\n this._dialogElement.style.top = dragStartTop + e.clientY - dragStartY + \"px\";\n this._dialogElement.style.left = dragStartLeft + e.clientX - dragStartX + \"px\";\n };\n const onDragEnd = () => {\n this._recorder.document.removeEventListener(\"mousemove\", onDragMove, true);\n this._recorder.document.removeEventListener(\"mouseup\", onDragEnd, true);\n };\n toolbarElement.addEventListener(\"mousedown\", (e) => {\n if (e.target.closest(\"x-pw-tool-item\")) return;\n dragStartX = e.clientX;\n dragStartY = e.clientY;\n dragStartTop = parseInt(this._dialogElement.style.top) || 0;\n dragStartLeft = parseInt(this._dialogElement.style.left) || 0;\n this._recorder.document.addEventListener(\"mousemove\", onDragMove, true);\n this._recorder.document.addEventListener(\"mouseup\", onDragEnd, true);\n e.stopPropagation();\n e.preventDefault();\n }, false);\n const consumeDialogEvent = (e) => {\n e.stopPropagation();\n };\n this._dialogElement.addEventListener(\"mousedown\", consumeDialogEvent, false);\n this._dialogElement.addEventListener(\"mouseup\", consumeDialogEvent, false);\n this._dialogElement.addEventListener(\"pointerdown\", consumeDialogEvent, false);\n this._dialogElement.addEventListener(\"pointerup\", consumeDialogEvent, false);\n this._dialogElement.addEventListener(\"click\", consumeDialogEvent, false);\n this._dialogElement.addEventListener(\"dblclick\", consumeDialogEvent, false);\n bodyElement.addEventListener(\"click\", consumeDialogEvent, false);\n this._recorder.highlight.appendChild(this._dialogElement);\n this._recorder.highlight.onGlassPaneClick(this._onGlassPaneClickHandler);\n this._recorder.document.addEventListener(\"keydown\", this._keyboardListener, true);\n return this._dialogElement;\n }\n moveTo(top, left) {\n if (!this._dialogElement)\n return;\n this._dialogElement.style.top = top + \"px\";\n this._dialogElement.style.left = left + \"px\";\n }\n close() {\n if (!this._dialogElement)\n return;\n this._dialogElement.remove();\n this._recorder.highlight.offGlassPaneClick(this._onGlassPaneClickHandler);\n this._recorder.document.removeEventListener(\"keydown\", this._keyboardListener);\n this._dialogElement = null;\n }\n};\nfunction deepActiveElement(document2) {\n let activeElement = document2.activeElement;\n while (activeElement && activeElement.shadowRoot && activeElement.shadowRoot.activeElement)\n activeElement = activeElement.shadowRoot.activeElement;\n return activeElement;\n}\nfunction modifiersForEvent(event) {\n return (event.altKey ? 1 : 0) | (event.ctrlKey ? 2 : 0) | (event.metaKey ? 4 : 0) | (event.shiftKey ? 8 : 0);\n}\nfunction buttonForEvent(event) {\n switch (event.which) {\n case 1:\n return \"left\";\n case 2:\n return \"middle\";\n case 3:\n return \"right\";\n }\n return \"left\";\n}\nfunction positionForEvent(event) {\n const targetElement = event.target;\n if (targetElement.nodeName !== \"CANVAS\")\n return;\n return {\n x: event.offsetX,\n y: event.offsetY\n };\n}\nfunction consumeEvent5(e) {\n e.preventDefault();\n e.stopPropagation();\n e.stopImmediatePropagation();\n}\nfunction asCheckbox(node) {\n if (!node || node.nodeName !== \"INPUT\")\n return null;\n const inputElement = node;\n return [\"checkbox\", \"radio\"].includes(inputElement.type) ? inputElement : null;\n}\nfunction isRangeInput(node) {\n if (!node || node.nodeName !== \"INPUT\")\n return false;\n const inputElement = node;\n return inputElement.type.toLowerCase() === \"range\";\n}\nfunction isPasswordInput(node) {\n if (!node || node.nodeName !== \"INPUT\")\n return false;\n return node.type.toLowerCase() === \"password\";\n}\nfunction addEventListener5(target, eventName, listener, useCapture) {\n target.addEventListener(eventName, listener, useCapture);\n const remove = () => {\n target.removeEventListener(eventName, listener, useCapture);\n };\n return remove;\n}\nfunction removeEventListeners3(listeners) {\n for (const listener of listeners)\n listener();\n listeners.splice(0, listeners.length);\n}\nfunction entriesForSelectorHighlight(injectedScript, language, selector, ownerDocument) {\n try {\n const parsedSelector = injectedScript.parseSelector(selector);\n const elements = injectedScript.querySelectorAll(parsedSelector, ownerDocument);\n const color = elements.length > 1 ? HighlightColors2.multiple : HighlightColors2.single;\n const locator = injectedScript.utils.asLocator(language, selector);\n return elements.map((element, index) => {\n const suffix = elements.length > 1 ? ` [${index + 1} of ${elements.length}]` : \"\";\n return { element, color, tooltipText: locator + suffix };\n });\n } catch (e) {\n return [];\n }\n}\nfunction createSvgElement(doc, { tagName, attrs, children }) {\n const elem = doc.createElementNS(\"http://www.w3.org/2000/svg\", tagName);\n if (attrs) {\n for (const [k, v] of Object.entries(attrs))\n elem.setAttribute(k, v);\n }\n if (children) {\n for (const c of children)\n elem.appendChild(createSvgElement(doc, c));\n }\n return elem;\n}\nfunction isAssertAction(action) {\n return action.name.startsWith(\"assert\");\n}\nfunction isLikelyMenuTrigger(el) {\n const tag = el.tagName;\n if (tag === \"BUTTON\" || tag === \"A\")\n return true;\n if (el.hasAttribute(\"aria-haspopup\") || el.hasAttribute(\"aria-expanded\"))\n return true;\n const role = el.getAttribute(\"role\");\n if (role && (role === \"button\" || role === \"link\" || role === \"menuitem\" || role === \"tab\"))\n return true;\n return false;\n}\nfunction closestMenuTrigger(start, document2) {\n let current = start;\n while (current && current !== document2.body && current !== document2.documentElement) {\n if (isLikelyMenuTrigger(current))\n return current;\n current = current.parentElement;\n }\n return null;\n}\nfunction getTimestamp10(recorder) {\n return recorder.injectedScript.utils.builtins.Date.now().toString();\n}\n\n// packages/injected/src/recorder/pollingRecorder.ts\nvar PollingRecorder = class {\n constructor(injectedScript, options) {\n this._recorder = new Recorder(injectedScript, options);\n this._embedder = injectedScript.window;\n injectedScript.onGlobalListenersRemoved.add(() => this._recorder.installListeners());\n const refreshOverlay = () => {\n this._lastStateJSON = void 0;\n this._pollRecorderMode().catch((e) => console.log(e));\n };\n this._embedder.__pw_refreshOverlay = refreshOverlay;\n injectedScript.window.__pw_recorderGenerateSelector = (element, options2) => {\n return injectedScript.generateSelector(element, {\n testIdAttributeName: (options2 == null ? void 0 : options2.testIdAttributeName) || \"data-testid\"\n });\n };\n injectedScript.window.__pw_computeScopedSelector = (element, options2) => {\n return computeScopedSelector(injectedScript, element, (options2 == null ? void 0 : options2.testIdAttributeName) || \"data-testid\");\n };\n refreshOverlay();\n }\n async _pollRecorderMode() {\n const pollPeriod = 1e3;\n if (this._pollRecorderModeTimer)\n this._recorder.injectedScript.utils.builtins.clearTimeout(this._pollRecorderModeTimer);\n const state = await this._embedder.__pw_recorderState().catch(() => null);\n if (!state) {\n this._pollRecorderModeTimer = this._recorder.injectedScript.utils.builtins.setTimeout(() => this._pollRecorderMode(), pollPeriod);\n return;\n }\n const stringifiedState = JSON.stringify(state);\n if (this._lastStateJSON !== stringifiedState) {\n this._lastStateJSON = stringifiedState;\n const win = this._recorder.document.defaultView;\n if (win.top !== win) {\n state.actionPoint = void 0;\n }\n this._recorder.setUIState(state, this);\n }\n this._pollRecorderModeTimer = this._recorder.injectedScript.utils.builtins.setTimeout(() => this._pollRecorderMode(), pollPeriod);\n }\n async performAction(action) {\n await this._embedder.__pw_recorderPerformAction(action);\n }\n async recordAction(action) {\n await this._embedder.__pw_recorderRecordAction(action);\n }\n async elementPicked(elementInfo) {\n await this._embedder.__pw_recorderElementPicked(elementInfo);\n }\n async setMode(mode) {\n await this._embedder.__pw_recorderSetMode(mode);\n }\n async setOverlayState(state) {\n await this._embedder.__pw_recorderSetOverlayState(state);\n }\n};\nvar pollingRecorder_default = PollingRecorder;\n";