@skyramp/mcp 0.4.4 → 0.5.0-rc.10
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.
- package/build/entitlements/entitlementGate.d.ts +8 -0
- package/build/entitlements/entitlementGate.js +57 -0
- package/build/entitlements/runCodes.d.ts +18 -0
- package/build/entitlements/runCodes.js +16 -0
- package/build/index.js +32 -6
- package/build/playwright/PlaywrightTraceService.d.ts +17 -0
- package/build/playwright/PlaywrightTraceService.js +29 -1
- package/build/playwright/dataSafetyConfig.d.ts +74 -0
- package/build/playwright/dataSafetyConfig.js +109 -0
- package/build/playwright/essentialTools.d.ts +1 -0
- package/build/playwright/essentialTools.js +44 -0
- package/build/playwright/registerPlaywrightTools.d.ts +36 -1
- package/build/playwright/registerPlaywrightTools.js +87 -43
- package/build/playwright/safeResolveDataSafety.d.ts +24 -0
- package/build/playwright/safeResolveDataSafety.js +33 -0
- package/build/prompts/initialize-workspace/initializeWorkspacePrompt.d.ts +17 -0
- package/build/prompts/initialize-workspace/initializeWorkspacePrompt.js +46 -177
- package/build/prompts/modularization/ui-test-modularization.d.ts +1 -0
- package/build/prompts/modularization/ui-test-modularization.js +9 -2
- package/build/prompts/test-maintenance/driftAnalysisShared.js +1 -1
- package/build/services/FleetExecutionService.d.ts +87 -16
- package/build/services/FleetExecutionService.js +487 -48
- package/build/services/FleetRunRegistry.d.ts +49 -0
- package/build/services/FleetRunRegistry.js +178 -0
- package/build/services/containerEnv.js +8 -2
- package/build/services/fleetLimitConfirmation.d.ts +19 -0
- package/build/services/fleetLimitConfirmation.js +126 -0
- package/build/tool-phases.d.ts +14 -8
- package/build/tool-phases.js +88 -43
- package/build/tools/code-refactor/assertion-state.js +3 -1
- package/build/tools/code-refactor/codeReuseTool.js +2 -1
- package/build/tools/code-refactor/utils-verify-gates.js +1 -1
- package/build/tools/executeFleetTool.d.ts +6 -4
- package/build/tools/executeFleetTool.js +67 -23
- package/build/tools/fixErrorTool.js +2 -1
- package/build/tools/fleetLifecycleTools.d.ts +20 -0
- package/build/tools/fleetLifecycleTools.js +124 -0
- package/build/tools/generate-tests/generateIntegrationRestTool.d.ts +38 -0
- package/build/tools/generate-tests/generateIntegrationRestTool.js +7 -1
- package/build/tools/generate-tests/generateUILoadRestTool.d.ts +19 -1
- package/build/tools/generate-tests/generateUILoadRestTool.js +316 -50
- package/build/tools/submitReportTool.js +4 -122
- package/build/tools/test-management/registerTestPlanTool.js +22 -6
- package/build/tools/workspace/initScanWorkspaceTool.d.ts +11 -1
- package/build/tools/workspace/initScanWorkspaceTool.js +111 -9
- package/build/tools/workspace/initializeWorkspaceTool.js +40 -4
- package/build/types/AssertionOutcome.d.ts +3 -0
- package/build/types/TestbotReport.d.ts +0 -37
- package/build/types/TestbotReport.js +0 -22
- package/build/types/index.d.ts +1 -1
- package/build/types/index.js +1 -1
- package/build/utils/assertion-verify/created-add-guard.d.ts +12 -0
- package/build/utils/assertion-verify/created-add-guard.js +173 -0
- package/build/utils/assertion-verify/index.d.ts +1 -0
- package/build/utils/assertion-verify/index.js +1 -0
- package/build/utils/assertion-verify/verify.d.ts +7 -3
- package/build/utils/assertion-verify/verify.js +9 -3
- package/build/utils/featureFlags.js +6 -3
- package/build/utils/repositorySlug.d.ts +14 -0
- package/build/utils/repositorySlug.js +46 -1
- package/build/utils/telemetry.d.ts +2 -0
- package/build/utils/telemetry.js +12 -0
- package/build/utils/utils-verify/action-key.d.ts +59 -2
- package/build/utils/utils-verify/action-key.js +162 -6
- package/build/utils/utils-verify/action-sites.d.ts +7 -0
- package/build/utils/utils-verify/action-sites.js +277 -35
- package/build/utils/utils-verify/call-sites.d.ts +8 -2
- package/build/utils/utils-verify/language-spec.js +1 -1
- package/build/utils/utils-verify/parse.d.ts +4 -0
- package/build/utils/utils-verify/parse.js +2 -1
- package/build/utils/versions.d.ts +3 -3
- package/build/utils/versions.js +1 -1
- package/build/workspace/workspace.d.ts +63 -121
- package/build/workspace/workspace.js +45 -48
- package/build/workspace/workspaceVerifier.d.ts +22 -0
- package/build/workspace/workspaceVerifier.js +65 -0
- package/node_modules/playwright/lib/dom-analyzer/blueprint.js +7 -6
- package/node_modules/playwright/lib/dom-analyzer/dynamicId.js +5 -4
- package/node_modules/playwright/lib/dom-analyzer/dynamicId.test.js +12 -0
- package/node_modules/playwright/lib/mcp/skyramp/dataSafetyGuard.js +822 -0
- package/node_modules/playwright/lib/mcp/skyramp/dataSafetySession.js +372 -0
- package/node_modules/playwright/lib/mcp/skyramp/exportTool.js +101 -8
- package/node_modules/playwright/lib/mcp/skyramp/finishTestSetupTool.js +45 -0
- package/node_modules/playwright/lib/mcp/skyramp/index.js +16 -0
- package/node_modules/playwright/lib/mcp/skyramp/recordingPhase.js +65 -0
- package/node_modules/playwright/lib/mcp/skyramp/skyRampImport.js +1 -0
- package/node_modules/playwright/lib/mcp/skyramp/traceRecordingBackend.js +598 -127
- package/node_modules/playwright/lib/mcp/test/skyRampExport.js +85 -31
- package/node_modules/playwright/lib/mcp/test/skyRampExport.test.js +132 -5
- package/node_modules/playwright/node_modules/playwright-core/lib/cli/program.js +2 -0
- package/node_modules/playwright/node_modules/playwright-core/lib/client/browserContext.js +4 -2
- package/node_modules/playwright/node_modules/playwright-core/lib/generated/injectedScriptSource.js +1 -1
- package/node_modules/playwright/node_modules/playwright-core/lib/generated/pollingRecorderSource.js +1 -1
- package/node_modules/playwright/node_modules/playwright-core/lib/protocol/validator.js +2 -1
- package/node_modules/playwright/node_modules/playwright-core/lib/server/browserContext.js +17 -1
- package/node_modules/playwright/node_modules/playwright-core/lib/server/codegen/skyramp/jsonlReader.js +1 -0
- package/node_modules/playwright/node_modules/playwright-core/lib/server/har/harRecorder.js +1 -0
- package/node_modules/playwright/node_modules/playwright-core/lib/server/har/harTracer.js +1 -1
- package/node_modules/playwright/node_modules/playwright-core/lib/server/recorder/recorderRunner.js +2 -1
- package/node_modules/playwright/node_modules/playwright-core/lib/server/recorder/recorderSignalProcessor.js +2 -1
- package/node_modules/playwright/node_modules/playwright-core/lib/server/recorder/skyramp/clickExpandedState.js +75 -0
- package/node_modules/playwright/node_modules/playwright-core/lib/server/recorder.js +4 -2
- package/node_modules/playwright/node_modules/playwright-core/lib/utils/isomorphic/volatileDate.js +85 -0
- package/node_modules/playwright/node_modules/playwright-core/lib/vite/traceViewer/assets/{codeMirrorModule-Bou8kKzE.js → codeMirrorModule-BuhAdckO.js} +1 -1
- package/node_modules/playwright/node_modules/playwright-core/lib/vite/traceViewer/assets/defaultSettingsView-COAHN430.js +809 -0
- package/node_modules/playwright/node_modules/playwright-core/lib/vite/traceViewer/{index.0MJW0uG7.js → index.7AU8h0PZ.js} +1 -1
- package/node_modules/playwright/node_modules/playwright-core/lib/vite/traceViewer/index.html +2 -2
- package/node_modules/playwright/node_modules/playwright-core/lib/vite/traceViewer/{uiMode.GLqsx5cI.js → uiMode.Dl7smBJn.js} +1 -1
- package/node_modules/playwright/node_modules/playwright-core/lib/vite/traceViewer/uiMode.html +2 -2
- package/node_modules/playwright/node_modules/playwright-core/package.json +1 -1
- package/node_modules/playwright/node_modules/playwright-core/src/cli/program.ts +5 -0
- package/node_modules/playwright/node_modules/playwright-core/src/client/browserContext.ts +3 -1
- package/node_modules/playwright/node_modules/playwright-core/src/client/types.ts +2 -0
- package/node_modules/playwright/node_modules/playwright-core/src/generated/injectedScriptSource.ts +1 -1
- package/node_modules/playwright/node_modules/playwright-core/src/generated/pollingRecorderSource.ts +1 -1
- package/node_modules/playwright/node_modules/playwright-core/src/protocol/validator.ts +1 -0
- package/node_modules/playwright/node_modules/playwright-core/src/server/browserContext.ts +25 -1
- package/node_modules/playwright/node_modules/playwright-core/src/server/codegen/skyramp/jsonlReader.ts +2 -0
- package/node_modules/playwright/node_modules/playwright-core/src/server/har/harRecorder.ts +1 -0
- package/node_modules/playwright/node_modules/playwright-core/src/server/har/harTracer.ts +3 -1
- package/node_modules/playwright/node_modules/playwright-core/src/server/recorder/recorderRunner.ts +3 -1
- package/node_modules/playwright/node_modules/playwright-core/src/server/recorder/recorderSignalProcessor.ts +1 -0
- package/node_modules/playwright/node_modules/playwright-core/src/server/recorder/skyramp/clickExpandedState.ts +82 -0
- package/node_modules/playwright/node_modules/playwright-core/src/server/recorder.ts +2 -0
- package/node_modules/playwright/node_modules/playwright-core/src/utils/isomorphic/volatileDate.ts +137 -1
- package/node_modules/playwright/package.json +1 -1
- package/package.json +4 -4
- package/plugin/prompts/plan-tests.md +2 -2
- package/plugin/prompts/testbot-task1.md +1 -1
- package/plugin/skills/enhance-assertions/reference/integration.md +1 -0
- package/plugin/skills/setup-testbot-target-sut/SKILL.md +60 -0
- package/plugin/skills/setup-testbot-target-sut/reference/ci-workflow.md +26 -0
- package/plugin/skills/setup-testbot-target-sut/reference/deployed-environment.md +105 -0
- package/plugin/skills/setup-testbot-target-sut/reference/existing-tests.md +14 -0
- package/plugin/skills/setup-testbot-target-sut/reference/troubleshooting.md +20 -0
- package/build/prompts/sut-setup/modes/adaptWorkflowPrompt.d.ts +0 -2
- package/build/prompts/sut-setup/modes/adaptWorkflowPrompt.js +0 -178
- package/build/prompts/sut-setup/modes/dockerComposePrompt.d.ts +0 -2
- package/build/prompts/sut-setup/modes/dockerComposePrompt.js +0 -311
- package/build/prompts/sut-setup/shared.d.ts +0 -62
- package/build/prompts/sut-setup/shared.js +0 -196
- package/build/resources/sutSetupResource.d.ts +0 -2
- package/build/resources/sutSetupResource.js +0 -63
- package/node_modules/playwright/node_modules/playwright-core/lib/vite/traceViewer/assets/defaultSettingsView-DTp2-8Si.js +0 -809
package/node_modules/playwright/node_modules/playwright-core/src/generated/pollingRecorderSource.ts
CHANGED
|
@@ -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 (/«r[0-9a-z]*»|(^|[-_])_?r_[0-9a-z]+_/.test(id)) 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;\">×</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, \""\")}\"\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";
|
|
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/stringUtils.ts\nfunction escapeRegExp(s) {\n return s.replace(/[.*+?^${}()|[\\]\\\\]/g, \"\\\\$&\");\n}\nvar ansiRegex = new RegExp(\"([\\\\u001B\\\\u009B][[\\\\]()#;?]*(?:(?:(?:[a-zA-Z\\\\d]*(?:;[-a-zA-Z\\\\d\\\\/#&.:=?%@~_]*)*)?\\\\u0007)|(?:(?:\\\\d{1,4}(?:;\\\\d{0,4})*)?[\\\\dA-PR-TZcf-ntqry=><~])))\", \"g\");\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\");\nvar kFallbackLocales = [\"en\", \"ja\", \"zh\", \"ko\", \"de\", \"fr\", \"es\", \"it\", \"pt\", \"nl\", \"ru\", \"pl\", \"sv\"];\nvar kDayMs = 864e5;\nvar kSunday4Jan2026 = 17674848e5;\nvar kMid2026Months = [17684352e5, 17711136e5, 17735328e5, 17762112e5, 17788032e5, 17814816e5, 17840736e5, 1786752e6, 17894304e5, 17920224e5, 17947008e5, 17972928e5];\nvar kLocaleWordsCache = /* @__PURE__ */ new Map();\nfunction escapeWords(s) {\n return s.replace(/[.*+?^${}()|[\\]\\\\]/g, \"\\\\$&\").replace(/\\\\\\./g, \"\\\\.?\").replace(/\\s+/g, \"\\\\s+\");\n}\nfunction localeWords(intl, locales) {\n var _a, _b, _c, _d;\n const key = locales.join(\",\");\n const cached = (_a = kLocaleWordsCache.get(key)) == null ? void 0 : _a.find((c) => c.intl === intl);\n if (cached)\n return cached.words;\n const relative = /* @__PURE__ */ new Set();\n const months = /* @__PURE__ */ new Set();\n const weekdays = /* @__PURE__ */ new Set();\n const dayWords = /* @__PURE__ */ new Set();\n for (const locale of locales) {\n try {\n const auto = new intl.RelativeTimeFormat(locale, { numeric: \"auto\" });\n for (const v of [-1, 0, 1])\n dayWords.add(auto.format(v, \"day\").toLowerCase());\n for (const style of [\"long\", \"short\", \"narrow\"]) {\n const always = new intl.RelativeTimeFormat(locale, { numeric: \"always\", style });\n for (const unit of [\"second\", \"minute\", \"hour\", \"day\", \"week\", \"month\", \"year\"]) {\n for (const v of [-1, -2, -5, 1, 2, 5]) {\n const one = Math.abs(v) === 1 && style === \"long\";\n relative.add(escapeWords(always.format(v, unit)).replace(/\\d+/g, one ? \"(?:\\\\d+|\\\\p{L}+)\" : \"\\\\d+\"));\n }\n }\n }\n for (const weekday of [\"long\", \"short\"]) {\n const format = new intl.DateTimeFormat(locale, { weekday, timeZone: \"UTC\" });\n for (let d = 4; d < 11; d++)\n weekdays.add(escapeWords(format.format(kSunday4Jan2026 + (d - 4) * kDayMs).replace(/\\.$/, \"\")));\n }\n for (const month2 of [\"long\", \"short\"]) {\n const format = new intl.DateTimeFormat(locale, { month: month2, timeZone: \"UTC\" });\n for (let m = 0; m < 12; m++) {\n const name = format.format(kMid2026Months[m]).replace(/\\.$/, \"\");\n if (/^\\p{L}{3,}$/u.test(name))\n months.add(escapeWords(name));\n }\n }\n } catch {\n }\n }\n const word = (s) => `(?<!\\\\p{L})(?:${s})(?!\\\\p{L})`;\n const month = word([...months].join(\"|\") || \"(?!)\");\n const words = {\n // A month name with its day next to it: \"Oct 4\", \"4. Okt.\", \"4 de octubre\", \"1er octobre\".\n monthDay: new RegExp(`${month}\\\\.?\\\\s*\\\\d{1,2}(?!\\\\d)|(?<!\\\\d)\\\\d{1,2}(?:er|\\\\.|\\xBA)?\\\\s+(?:de\\\\s+)?${month}`, \"iu\"),\n relative: new RegExp(word([...relative].join(\"|\") || \"(?!)\"), \"iu\"),\n dayWords,\n weekday: new RegExp(`^${word([...weekdays].join(\"|\") || \"(?!)\")}\\\\.?$`, \"iu\"),\n currencies: new Set((_c = (_b = intl.supportedValuesOf) == null ? void 0 : _b.call(intl, \"currency\")) != null ? _c : [])\n };\n kLocaleWordsCache.set(key, [...(_d = kLocaleWordsCache.get(key)) != null ? _d : [], { intl, words }]);\n return words;\n}\nfunction isVolatileValue(value, options) {\n var _a;\n const text = value.replace(/\\s+/g, \" \").trim();\n if (!text)\n return false;\n if (hasVolatileDateFragment(text.replace(/\\d{4}[-/]\\d{1,2}[-/]\\d{1,2}(?=[-/.]?\\d)/g, \"\")))\n return true;\n if (/\\b\\d{1,2}:\\d{2}\\b/.test(text))\n return true;\n if (/(?<![\\d./-])(?:\\d{4}[/-]\\d{1,2}[/-]\\d{1,2}|\\d{1,2}[./-]\\d{1,2}[./-]\\d{4}|\\d{1,2}\\/\\d{1,2}\\/\\d{2})(?![\\d]|[./-]\\d)/.test(text))\n return true;\n if (/\\d{4}\\s*[年년]\\s*\\d{1,2}\\s*[月월]|\\d{1,2}\\s*[月월]\\s*\\d{1,2}\\s*[日일]/.test(text))\n return true;\n const words = localeWords(options.intl, [...(_a = options.locales) != null ? _a : [], ...kFallbackLocales].filter(Boolean));\n if (words.monthDay.test(text) && /(?<!\\d)\\d{4}(?!\\d)/.test(text))\n return true;\n if (words.relative.test(text) || words.dayWords.has(text.toLowerCase()) || words.weekday.test(text))\n return true;\n const code = (c) => !!c && words.currencies.has(c) ? c : null;\n const amount = text.replace(/^(?:\\p{Sc}|[A-Z]{1,2}\\$)\\s*/u, \"\").replace(/^([A-Z]{3})(?=[\\s\\d])\\s*/u, (m, c) => code(c) ? \"\" : m).replace(/\\s*\\p{Sc}$/u, \"\").replace(/\\s([A-Z]{3})$/u, (m, c) => code(c) ? \"\" : m).replace(/(?<=\\d)[\\p{sc=Han}\\p{sc=Hangul}]$/u, \"\");\n if (!/^[+\\-−]?(?:\\d{1,3}(?:[.,\\s'’]\\d{3})+|\\d+)(?:[.,]\\d+)?\\s*%?$/.test(amount))\n return false;\n return amount !== text || /%$/.test(amount) || /^[+\\-−]?\\d{1,3}(?:[.,\\s'’]\\d{3})+/.test(amount);\n}\n\n// packages/injected/src/domUtils.ts\nvar globalOptions = {};\nfunction isInsideScope(scope, element) {\n while (element) {\n if (scope.contains(element))\n return true;\n element = enclosingShadowHost(element);\n }\n return false;\n}\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 getElementComputedStyle(element, pseudo) {\n const cache = pseudo === \"::before\" ? cacheStyleBefore : pseudo === \"::after\" ? cacheStyleAfter : cacheStyle;\n if (cache && cache.has(element))\n return cache.get(element);\n const style = element.ownerDocument && element.ownerDocument.defaultView ? element.ownerDocument.defaultView.getComputedStyle(element, pseudo) : void 0;\n cache == null ? void 0 : cache.set(element, style);\n return style;\n}\nfunction isElementStyleVisibilityVisible(element, style) {\n style = style != null ? style : getElementComputedStyle(element);\n if (!style)\n return true;\n if (Element.prototype.checkVisibility && globalOptions.browserNameForWorkarounds !== \"webkit\") {\n if (!element.checkVisibility())\n return false;\n } else {\n const detailsOrSummary = element.closest(\"details,summary\");\n if (detailsOrSummary !== element && (detailsOrSummary == null ? void 0 : detailsOrSummary.nodeName) === \"DETAILS\" && !detailsOrSummary.open)\n return false;\n }\n if (style.visibility !== \"visible\")\n return false;\n return true;\n}\nfunction computeBox(element) {\n const style = getElementComputedStyle(element);\n if (!style)\n return { visible: true, inline: false };\n const cursor = style.cursor;\n if (style.display === \"contents\") {\n for (let child = element.firstChild; child; child = child.nextSibling) {\n if (child.nodeType === 1 && isElementVisible(child))\n return { visible: true, inline: false, cursor };\n if (child.nodeType === 3 && isVisibleTextNode(child))\n return { visible: true, inline: true, cursor };\n }\n return { visible: false, inline: false, cursor };\n }\n if (!isElementStyleVisibilityVisible(element, style))\n return { cursor, visible: false, inline: false };\n const rect = element.getBoundingClientRect();\n return { cursor, visible: rect.width > 0 && rect.height > 0, inline: style.display === \"inline\" };\n}\nfunction isElementVisible(element) {\n return computeBox(element).visible;\n}\nfunction isVisibleTextNode(node) {\n const range = node.ownerDocument.createRange();\n range.selectNode(node);\n const rect = range.getBoundingClientRect();\n return rect.width > 0 && rect.height > 0;\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}\nvar cacheStyle;\nvar cacheStyleBefore;\nvar cacheStyleAfter;\n\n// packages/playwright-core/src/utils/isomorphic/cssTokenizer.ts\nvar between = function(num, first, last) {\n return num >= first && num <= last;\n};\nfunction digit(code) {\n return between(code, 48, 57);\n}\nfunction hexdigit(code) {\n return digit(code) || between(code, 65, 70) || between(code, 97, 102);\n}\nfunction uppercaseletter(code) {\n return between(code, 65, 90);\n}\nfunction lowercaseletter(code) {\n return between(code, 97, 122);\n}\nfunction letter(code) {\n return uppercaseletter(code) || lowercaseletter(code);\n}\nfunction nonascii(code) {\n return code >= 128;\n}\nfunction namestartchar(code) {\n return letter(code) || nonascii(code) || code === 95;\n}\nfunction namechar(code) {\n return namestartchar(code) || digit(code) || code === 45;\n}\nfunction nonprintable(code) {\n return between(code, 0, 8) || code === 11 || between(code, 14, 31) || code === 127;\n}\nfunction newline(code) {\n return code === 10;\n}\nfunction whitespace(code) {\n return newline(code) || code === 9 || code === 32;\n}\nvar maximumallowedcodepoint = 1114111;\nvar InvalidCharacterError = class extends Error {\n constructor(message) {\n super(message);\n this.name = \"InvalidCharacterError\";\n }\n};\nfunction preprocess(str) {\n const codepoints = [];\n for (let i = 0; i < str.length; i++) {\n let code = str.charCodeAt(i);\n if (code === 13 && str.charCodeAt(i + 1) === 10) {\n code = 10;\n i++;\n }\n if (code === 13 || code === 12)\n code = 10;\n if (code === 0)\n code = 65533;\n if (between(code, 55296, 56319) && between(str.charCodeAt(i + 1), 56320, 57343)) {\n const lead = code - 55296;\n const trail = str.charCodeAt(i + 1) - 56320;\n code = Math.pow(2, 16) + lead * Math.pow(2, 10) + trail;\n i++;\n }\n codepoints.push(code);\n }\n return codepoints;\n}\nfunction stringFromCode(code) {\n if (code <= 65535)\n return String.fromCharCode(code);\n code -= Math.pow(2, 16);\n const lead = Math.floor(code / Math.pow(2, 10)) + 55296;\n const trail = code % Math.pow(2, 10) + 56320;\n return String.fromCharCode(lead) + String.fromCharCode(trail);\n}\nfunction tokenize(str1) {\n const str = preprocess(str1);\n let i = -1;\n const tokens = [];\n let code;\n let line = 0;\n let column = 0;\n let lastLineLength = 0;\n const incrLineno = function() {\n line += 1;\n lastLineLength = column;\n column = 0;\n };\n const locStart = { line, column };\n const codepoint = function(i2) {\n if (i2 >= str.length)\n return -1;\n return str[i2];\n };\n const next = function(num) {\n if (num === void 0)\n num = 1;\n if (num > 3)\n throw \"Spec Error: no more than three codepoints of lookahead.\";\n return codepoint(i + num);\n };\n const consume = function(num) {\n if (num === void 0)\n num = 1;\n i += num;\n code = codepoint(i);\n if (newline(code))\n incrLineno();\n else\n column += num;\n return true;\n };\n const reconsume = function() {\n i -= 1;\n if (newline(code)) {\n line -= 1;\n column = lastLineLength;\n } else {\n column -= 1;\n }\n locStart.line = line;\n locStart.column = column;\n return true;\n };\n const eof = function(codepoint2) {\n if (codepoint2 === void 0)\n codepoint2 = code;\n return codepoint2 === -1;\n };\n const donothing = function() {\n };\n const parseerror = function() {\n };\n const consumeAToken = function() {\n consumeComments();\n consume();\n if (whitespace(code)) {\n while (whitespace(next()))\n consume();\n return new WhitespaceToken();\n } else if (code === 34) {\n return consumeAStringToken();\n } else if (code === 35) {\n if (namechar(next()) || areAValidEscape(next(1), next(2))) {\n const token = new HashToken(\"\");\n if (wouldStartAnIdentifier(next(1), next(2), next(3)))\n token.type = \"id\";\n token.value = consumeAName();\n return token;\n } else {\n return new DelimToken(code);\n }\n } else if (code === 36) {\n if (next() === 61) {\n consume();\n return new SuffixMatchToken();\n } else {\n return new DelimToken(code);\n }\n } else if (code === 39) {\n return consumeAStringToken();\n } else if (code === 40) {\n return new OpenParenToken();\n } else if (code === 41) {\n return new CloseParenToken();\n } else if (code === 42) {\n if (next() === 61) {\n consume();\n return new SubstringMatchToken();\n } else {\n return new DelimToken(code);\n }\n } else if (code === 43) {\n if (startsWithANumber()) {\n reconsume();\n return consumeANumericToken();\n } else {\n return new DelimToken(code);\n }\n } else if (code === 44) {\n return new CommaToken();\n } else if (code === 45) {\n if (startsWithANumber()) {\n reconsume();\n return consumeANumericToken();\n } else if (next(1) === 45 && next(2) === 62) {\n consume(2);\n return new CDCToken();\n } else if (startsWithAnIdentifier()) {\n reconsume();\n return consumeAnIdentlikeToken();\n } else {\n return new DelimToken(code);\n }\n } else if (code === 46) {\n if (startsWithANumber()) {\n reconsume();\n return consumeANumericToken();\n } else {\n return new DelimToken(code);\n }\n } else if (code === 58) {\n return new ColonToken();\n } else if (code === 59) {\n return new SemicolonToken();\n } else if (code === 60) {\n if (next(1) === 33 && next(2) === 45 && next(3) === 45) {\n consume(3);\n return new CDOToken();\n } else {\n return new DelimToken(code);\n }\n } else if (code === 64) {\n if (wouldStartAnIdentifier(next(1), next(2), next(3)))\n return new AtKeywordToken(consumeAName());\n else\n return new DelimToken(code);\n } else if (code === 91) {\n return new OpenSquareToken();\n } else if (code === 92) {\n if (startsWithAValidEscape()) {\n reconsume();\n return consumeAnIdentlikeToken();\n } else {\n parseerror();\n return new DelimToken(code);\n }\n } else if (code === 93) {\n return new CloseSquareToken();\n } else if (code === 94) {\n if (next() === 61) {\n consume();\n return new PrefixMatchToken();\n } else {\n return new DelimToken(code);\n }\n } else if (code === 123) {\n return new OpenCurlyToken();\n } else if (code === 124) {\n if (next() === 61) {\n consume();\n return new DashMatchToken();\n } else if (next() === 124) {\n consume();\n return new ColumnToken();\n } else {\n return new DelimToken(code);\n }\n } else if (code === 125) {\n return new CloseCurlyToken();\n } else if (code === 126) {\n if (next() === 61) {\n consume();\n return new IncludeMatchToken();\n } else {\n return new DelimToken(code);\n }\n } else if (digit(code)) {\n reconsume();\n return consumeANumericToken();\n } else if (namestartchar(code)) {\n reconsume();\n return consumeAnIdentlikeToken();\n } else if (eof()) {\n return new EOFToken();\n } else {\n return new DelimToken(code);\n }\n };\n const consumeComments = function() {\n while (next(1) === 47 && next(2) === 42) {\n consume(2);\n while (true) {\n consume();\n if (code === 42 && next() === 47) {\n consume();\n break;\n } else if (eof()) {\n parseerror();\n return;\n }\n }\n }\n };\n const consumeANumericToken = function() {\n const num = consumeANumber();\n if (wouldStartAnIdentifier(next(1), next(2), next(3))) {\n const token = new DimensionToken();\n token.value = num.value;\n token.repr = num.repr;\n token.type = num.type;\n token.unit = consumeAName();\n return token;\n } else if (next() === 37) {\n consume();\n const token = new PercentageToken();\n token.value = num.value;\n token.repr = num.repr;\n return token;\n } else {\n const token = new NumberToken();\n token.value = num.value;\n token.repr = num.repr;\n token.type = num.type;\n return token;\n }\n };\n const consumeAnIdentlikeToken = function() {\n const str2 = consumeAName();\n if (str2.toLowerCase() === \"url\" && next() === 40) {\n consume();\n while (whitespace(next(1)) && whitespace(next(2)))\n consume();\n if (next() === 34 || next() === 39)\n return new FunctionToken(str2);\n else if (whitespace(next()) && (next(2) === 34 || next(2) === 39))\n return new FunctionToken(str2);\n else\n return consumeAURLToken();\n } else if (next() === 40) {\n consume();\n return new FunctionToken(str2);\n } else {\n return new IdentToken(str2);\n }\n };\n const consumeAStringToken = function(endingCodePoint) {\n if (endingCodePoint === void 0)\n endingCodePoint = code;\n let string = \"\";\n while (consume()) {\n if (code === endingCodePoint || eof()) {\n return new StringToken(string);\n } else if (newline(code)) {\n parseerror();\n reconsume();\n return new BadStringToken();\n } else if (code === 92) {\n if (eof(next()))\n donothing();\n else if (newline(next()))\n consume();\n else\n string += stringFromCode(consumeEscape());\n } else {\n string += stringFromCode(code);\n }\n }\n throw new Error(\"Internal error\");\n };\n const consumeAURLToken = function() {\n const token = new URLToken(\"\");\n while (whitespace(next()))\n consume();\n if (eof(next()))\n return token;\n while (consume()) {\n if (code === 41 || eof()) {\n return token;\n } else if (whitespace(code)) {\n while (whitespace(next()))\n consume();\n if (next() === 41 || eof(next())) {\n consume();\n return token;\n } else {\n consumeTheRemnantsOfABadURL();\n return new BadURLToken();\n }\n } else if (code === 34 || code === 39 || code === 40 || nonprintable(code)) {\n parseerror();\n consumeTheRemnantsOfABadURL();\n return new BadURLToken();\n } else if (code === 92) {\n if (startsWithAValidEscape()) {\n token.value += stringFromCode(consumeEscape());\n } else {\n parseerror();\n consumeTheRemnantsOfABadURL();\n return new BadURLToken();\n }\n } else {\n token.value += stringFromCode(code);\n }\n }\n throw new Error(\"Internal error\");\n };\n const consumeEscape = function() {\n consume();\n if (hexdigit(code)) {\n const digits = [code];\n for (let total = 0; total < 5; total++) {\n if (hexdigit(next())) {\n consume();\n digits.push(code);\n } else {\n break;\n }\n }\n if (whitespace(next()))\n consume();\n let value = parseInt(digits.map(function(x) {\n return String.fromCharCode(x);\n }).join(\"\"), 16);\n if (value > maximumallowedcodepoint)\n value = 65533;\n return value;\n } else if (eof()) {\n return 65533;\n } else {\n return code;\n }\n };\n const areAValidEscape = function(c1, c2) {\n if (c1 !== 92)\n return false;\n if (newline(c2))\n return false;\n return true;\n };\n const startsWithAValidEscape = function() {\n return areAValidEscape(code, next());\n };\n const wouldStartAnIdentifier = function(c1, c2, c3) {\n if (c1 === 45)\n return namestartchar(c2) || c2 === 45 || areAValidEscape(c2, c3);\n else if (namestartchar(c1))\n return true;\n else if (c1 === 92)\n return areAValidEscape(c1, c2);\n else\n return false;\n };\n const startsWithAnIdentifier = function() {\n return wouldStartAnIdentifier(code, next(1), next(2));\n };\n const wouldStartANumber = function(c1, c2, c3) {\n if (c1 === 43 || c1 === 45) {\n if (digit(c2))\n return true;\n if (c2 === 46 && digit(c3))\n return true;\n return false;\n } else if (c1 === 46) {\n if (digit(c2))\n return true;\n return false;\n } else if (digit(c1)) {\n return true;\n } else {\n return false;\n }\n };\n const startsWithANumber = function() {\n return wouldStartANumber(code, next(1), next(2));\n };\n const consumeAName = function() {\n let result = \"\";\n while (consume()) {\n if (namechar(code)) {\n result += stringFromCode(code);\n } else if (startsWithAValidEscape()) {\n result += stringFromCode(consumeEscape());\n } else {\n reconsume();\n return result;\n }\n }\n throw new Error(\"Internal parse error\");\n };\n const consumeANumber = function() {\n let repr = \"\";\n let type = \"integer\";\n if (next() === 43 || next() === 45) {\n consume();\n repr += stringFromCode(code);\n }\n while (digit(next())) {\n consume();\n repr += stringFromCode(code);\n }\n if (next(1) === 46 && digit(next(2))) {\n consume();\n repr += stringFromCode(code);\n consume();\n repr += stringFromCode(code);\n type = \"number\";\n while (digit(next())) {\n consume();\n repr += stringFromCode(code);\n }\n }\n const c1 = next(1), c2 = next(2), c3 = next(3);\n if ((c1 === 69 || c1 === 101) && digit(c2)) {\n consume();\n repr += stringFromCode(code);\n consume();\n repr += stringFromCode(code);\n type = \"number\";\n while (digit(next())) {\n consume();\n repr += stringFromCode(code);\n }\n } else if ((c1 === 69 || c1 === 101) && (c2 === 43 || c2 === 45) && digit(c3)) {\n consume();\n repr += stringFromCode(code);\n consume();\n repr += stringFromCode(code);\n consume();\n repr += stringFromCode(code);\n type = \"number\";\n while (digit(next())) {\n consume();\n repr += stringFromCode(code);\n }\n }\n const value = convertAStringToANumber(repr);\n return { type, value, repr };\n };\n const convertAStringToANumber = function(string) {\n return +string;\n };\n const consumeTheRemnantsOfABadURL = function() {\n while (consume()) {\n if (code === 41 || eof()) {\n return;\n } else if (startsWithAValidEscape()) {\n consumeEscape();\n donothing();\n } else {\n donothing();\n }\n }\n };\n let iterationCount = 0;\n while (!eof(next())) {\n tokens.push(consumeAToken());\n iterationCount++;\n if (iterationCount > str.length * 2)\n throw new Error(\"I'm infinite-looping!\");\n }\n return tokens;\n}\nvar CSSParserToken = class {\n constructor() {\n this.tokenType = \"\";\n }\n toJSON() {\n return { token: this.tokenType };\n }\n toString() {\n return this.tokenType;\n }\n toSource() {\n return \"\" + this;\n }\n};\nvar BadStringToken = class extends CSSParserToken {\n constructor() {\n super(...arguments);\n this.tokenType = \"BADSTRING\";\n }\n};\nvar BadURLToken = class extends CSSParserToken {\n constructor() {\n super(...arguments);\n this.tokenType = \"BADURL\";\n }\n};\nvar WhitespaceToken = class extends CSSParserToken {\n constructor() {\n super(...arguments);\n this.tokenType = \"WHITESPACE\";\n }\n toString() {\n return \"WS\";\n }\n toSource() {\n return \" \";\n }\n};\nvar CDOToken = class extends CSSParserToken {\n constructor() {\n super(...arguments);\n this.tokenType = \"CDO\";\n }\n toSource() {\n return \"<!--\";\n }\n};\nvar CDCToken = class extends CSSParserToken {\n constructor() {\n super(...arguments);\n this.tokenType = \"CDC\";\n }\n toSource() {\n return \"-->\";\n }\n};\nvar ColonToken = class extends CSSParserToken {\n constructor() {\n super(...arguments);\n this.tokenType = \":\";\n }\n};\nvar SemicolonToken = class extends CSSParserToken {\n constructor() {\n super(...arguments);\n this.tokenType = \";\";\n }\n};\nvar CommaToken = class extends CSSParserToken {\n constructor() {\n super(...arguments);\n this.tokenType = \",\";\n }\n};\nvar GroupingToken = class extends CSSParserToken {\n constructor() {\n super(...arguments);\n this.value = \"\";\n this.mirror = \"\";\n }\n};\nvar OpenCurlyToken = class extends GroupingToken {\n constructor() {\n super();\n this.tokenType = \"{\";\n this.value = \"{\";\n this.mirror = \"}\";\n }\n};\nvar CloseCurlyToken = class extends GroupingToken {\n constructor() {\n super();\n this.tokenType = \"}\";\n this.value = \"}\";\n this.mirror = \"{\";\n }\n};\nvar OpenSquareToken = class extends GroupingToken {\n constructor() {\n super();\n this.tokenType = \"[\";\n this.value = \"[\";\n this.mirror = \"]\";\n }\n};\nvar CloseSquareToken = class extends GroupingToken {\n constructor() {\n super();\n this.tokenType = \"]\";\n this.value = \"]\";\n this.mirror = \"[\";\n }\n};\nvar OpenParenToken = class extends GroupingToken {\n constructor() {\n super();\n this.tokenType = \"(\";\n this.value = \"(\";\n this.mirror = \")\";\n }\n};\nvar CloseParenToken = class extends GroupingToken {\n constructor() {\n super();\n this.tokenType = \")\";\n this.value = \")\";\n this.mirror = \"(\";\n }\n};\nvar IncludeMatchToken = class extends CSSParserToken {\n constructor() {\n super(...arguments);\n this.tokenType = \"~=\";\n }\n};\nvar DashMatchToken = class extends CSSParserToken {\n constructor() {\n super(...arguments);\n this.tokenType = \"|=\";\n }\n};\nvar PrefixMatchToken = class extends CSSParserToken {\n constructor() {\n super(...arguments);\n this.tokenType = \"^=\";\n }\n};\nvar SuffixMatchToken = class extends CSSParserToken {\n constructor() {\n super(...arguments);\n this.tokenType = \"$=\";\n }\n};\nvar SubstringMatchToken = class extends CSSParserToken {\n constructor() {\n super(...arguments);\n this.tokenType = \"*=\";\n }\n};\nvar ColumnToken = class extends CSSParserToken {\n constructor() {\n super(...arguments);\n this.tokenType = \"||\";\n }\n};\nvar EOFToken = class extends CSSParserToken {\n constructor() {\n super(...arguments);\n this.tokenType = \"EOF\";\n }\n toSource() {\n return \"\";\n }\n};\nvar DelimToken = class extends CSSParserToken {\n constructor(code) {\n super();\n this.tokenType = \"DELIM\";\n this.value = \"\";\n this.value = stringFromCode(code);\n }\n toString() {\n return \"DELIM(\" + this.value + \")\";\n }\n toJSON() {\n const json = this.constructor.prototype.constructor.prototype.toJSON.call(this);\n json.value = this.value;\n return json;\n }\n toSource() {\n if (this.value === \"\\\\\")\n return \"\\\\\\n\";\n else\n return this.value;\n }\n};\nvar StringValuedToken = class extends CSSParserToken {\n constructor() {\n super(...arguments);\n this.value = \"\";\n }\n ASCIIMatch(str) {\n return this.value.toLowerCase() === str.toLowerCase();\n }\n toJSON() {\n const json = this.constructor.prototype.constructor.prototype.toJSON.call(this);\n json.value = this.value;\n return json;\n }\n};\nvar IdentToken = class extends StringValuedToken {\n constructor(val) {\n super();\n this.tokenType = \"IDENT\";\n this.value = val;\n }\n toString() {\n return \"IDENT(\" + this.value + \")\";\n }\n toSource() {\n return escapeIdent(this.value);\n }\n};\nvar FunctionToken = class extends StringValuedToken {\n constructor(val) {\n super();\n this.tokenType = \"FUNCTION\";\n this.value = val;\n this.mirror = \")\";\n }\n toString() {\n return \"FUNCTION(\" + this.value + \")\";\n }\n toSource() {\n return escapeIdent(this.value) + \"(\";\n }\n};\nvar AtKeywordToken = class extends StringValuedToken {\n constructor(val) {\n super();\n this.tokenType = \"AT-KEYWORD\";\n this.value = val;\n }\n toString() {\n return \"AT(\" + this.value + \")\";\n }\n toSource() {\n return \"@\" + escapeIdent(this.value);\n }\n};\nvar HashToken = class extends StringValuedToken {\n constructor(val) {\n super();\n this.tokenType = \"HASH\";\n this.value = val;\n this.type = \"unrestricted\";\n }\n toString() {\n return \"HASH(\" + this.value + \")\";\n }\n toJSON() {\n const json = this.constructor.prototype.constructor.prototype.toJSON.call(this);\n json.value = this.value;\n json.type = this.type;\n return json;\n }\n toSource() {\n if (this.type === \"id\")\n return \"#\" + escapeIdent(this.value);\n else\n return \"#\" + escapeHash(this.value);\n }\n};\nvar StringToken = class extends StringValuedToken {\n constructor(val) {\n super();\n this.tokenType = \"STRING\";\n this.value = val;\n }\n toString() {\n return '\"' + escapeString(this.value) + '\"';\n }\n};\nvar URLToken = class extends StringValuedToken {\n constructor(val) {\n super();\n this.tokenType = \"URL\";\n this.value = val;\n }\n toString() {\n return \"URL(\" + this.value + \")\";\n }\n toSource() {\n return 'url(\"' + escapeString(this.value) + '\")';\n }\n};\nvar NumberToken = class extends CSSParserToken {\n constructor() {\n super();\n this.tokenType = \"NUMBER\";\n this.type = \"integer\";\n this.repr = \"\";\n }\n toString() {\n if (this.type === \"integer\")\n return \"INT(\" + this.value + \")\";\n return \"NUMBER(\" + this.value + \")\";\n }\n toJSON() {\n const json = super.toJSON();\n json.value = this.value;\n json.type = this.type;\n json.repr = this.repr;\n return json;\n }\n toSource() {\n return this.repr;\n }\n};\nvar PercentageToken = class extends CSSParserToken {\n constructor() {\n super();\n this.tokenType = \"PERCENTAGE\";\n this.repr = \"\";\n }\n toString() {\n return \"PERCENTAGE(\" + this.value + \")\";\n }\n toJSON() {\n const json = this.constructor.prototype.constructor.prototype.toJSON.call(this);\n json.value = this.value;\n json.repr = this.repr;\n return json;\n }\n toSource() {\n return this.repr + \"%\";\n }\n};\nvar DimensionToken = class extends CSSParserToken {\n constructor() {\n super();\n this.tokenType = \"DIMENSION\";\n this.type = \"integer\";\n this.repr = \"\";\n this.unit = \"\";\n }\n toString() {\n return \"DIM(\" + this.value + \",\" + this.unit + \")\";\n }\n toJSON() {\n const json = this.constructor.prototype.constructor.prototype.toJSON.call(this);\n json.value = this.value;\n json.type = this.type;\n json.repr = this.repr;\n json.unit = this.unit;\n return json;\n }\n toSource() {\n const source = this.repr;\n let unit = escapeIdent(this.unit);\n if (unit[0].toLowerCase() === \"e\" && (unit[1] === \"-\" || between(unit.charCodeAt(1), 48, 57))) {\n unit = \"\\\\65 \" + unit.slice(1, unit.length);\n }\n return source + unit;\n }\n};\nfunction escapeIdent(string) {\n string = \"\" + string;\n let result = \"\";\n const firstcode = string.charCodeAt(0);\n for (let i = 0; i < string.length; i++) {\n const code = string.charCodeAt(i);\n if (code === 0)\n throw new InvalidCharacterError(\"Invalid character: the input contains U+0000.\");\n if (between(code, 1, 31) || code === 127 || i === 0 && between(code, 48, 57) || i === 1 && between(code, 48, 57) && firstcode === 45)\n result += \"\\\\\" + code.toString(16) + \" \";\n else if (code >= 128 || code === 45 || code === 95 || between(code, 48, 57) || between(code, 65, 90) || between(code, 97, 122))\n result += string[i];\n else\n result += \"\\\\\" + string[i];\n }\n return result;\n}\nfunction escapeHash(string) {\n string = \"\" + string;\n let result = \"\";\n for (let i = 0; i < string.length; i++) {\n const code = string.charCodeAt(i);\n if (code === 0)\n throw new InvalidCharacterError(\"Invalid character: the input contains U+0000.\");\n if (code >= 128 || code === 45 || code === 95 || between(code, 48, 57) || between(code, 65, 90) || between(code, 97, 122))\n result += string[i];\n else\n result += \"\\\\\" + code.toString(16) + \" \";\n }\n return result;\n}\nfunction escapeString(string) {\n string = \"\" + string;\n let result = \"\";\n for (let i = 0; i < string.length; i++) {\n const code = string.charCodeAt(i);\n if (code === 0)\n throw new InvalidCharacterError(\"Invalid character: the input contains U+0000.\");\n if (between(code, 1, 31) || code === 127)\n result += \"\\\\\" + code.toString(16) + \" \";\n else if (code === 34 || code === 92)\n result += \"\\\\\" + string[i];\n else\n result += string[i];\n }\n return result;\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 getAriaBoolean(attr) {\n return attr === null ? void 0 : attr.toLowerCase() === \"true\";\n}\nfunction isElementIgnoredForAria(element) {\n return [\"STYLE\", \"SCRIPT\", \"NOSCRIPT\", \"TEMPLATE\"].includes(elementSafeTagName(element));\n}\nfunction isElementHiddenForAria(element) {\n if (isElementIgnoredForAria(element))\n return true;\n const style = getElementComputedStyle(element);\n const isSlot = element.nodeName === \"SLOT\";\n if ((style == null ? void 0 : style.display) === \"contents\" && !isSlot) {\n for (let child = element.firstChild; child; child = child.nextSibling) {\n if (child.nodeType === 1 && !isElementHiddenForAria(child))\n return false;\n if (child.nodeType === 3 && isVisibleTextNode(child))\n return false;\n }\n return true;\n }\n const isOptionInsideSelect = element.nodeName === \"OPTION\" && !!element.closest(\"select\");\n if (!isOptionInsideSelect && !isSlot && !isElementStyleVisibilityVisible(element, style))\n return true;\n return belongsToDisplayNoneOrAriaHiddenOrNonSlotted(element);\n}\nfunction belongsToDisplayNoneOrAriaHiddenOrNonSlotted(element) {\n let hidden = cacheIsHidden == null ? void 0 : cacheIsHidden.get(element);\n if (hidden === void 0) {\n hidden = false;\n if (element.parentElement && element.parentElement.shadowRoot && !element.assignedSlot)\n hidden = true;\n if (!hidden) {\n const style = getElementComputedStyle(element);\n hidden = !style || style.display === \"none\" || getAriaBoolean(element.getAttribute(\"aria-hidden\")) === true;\n }\n if (!hidden) {\n const parent = parentElementOrShadowHost(element);\n if (parent)\n hidden = belongsToDisplayNoneOrAriaHiddenOrNonSlotted(parent);\n }\n cacheIsHidden == null ? void 0 : cacheIsHidden.set(element, hidden);\n }\n return hidden;\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 trimFlatString(s) {\n return s.trim();\n}\nfunction asFlatString(s) {\n return s.split(\"\\xA0\").map((chunk) => chunk.replace(/\\r\\n/g, \"\\n\").replace(/[\\u200b\\u00ad]/g, \"\").replace(/\\s\\s*/g, \" \")).join(\"\\xA0\").trim();\n}\nfunction queryInAriaOwned(element, selector) {\n const result = [...element.querySelectorAll(selector)];\n for (const owned of getIdRefs(element, element.getAttribute(\"aria-owns\"))) {\n if (owned.matches(selector))\n result.push(owned);\n result.push(...owned.querySelectorAll(selector));\n }\n return result;\n}\nfunction getCSSContent(element, pseudo) {\n const cache = pseudo === \"::before\" ? cachePseudoContentBefore : pseudo === \"::after\" ? cachePseudoContentAfter : cachePseudoContent;\n if (cache == null ? void 0 : cache.has(element))\n return cache == null ? void 0 : cache.get(element);\n const style = getElementComputedStyle(element, pseudo);\n let content;\n if (style) {\n const contentValue = style.content;\n if (contentValue && contentValue !== \"none\" && contentValue !== \"normal\") {\n if (style.display !== \"none\" && style.visibility !== \"hidden\") {\n content = parseCSSContentPropertyAsString(element, contentValue, !!pseudo);\n }\n }\n }\n if (pseudo && content !== void 0) {\n const display = (style == null ? void 0 : style.display) || \"inline\";\n if (display !== \"inline\")\n content = \" \" + content + \" \";\n }\n if (cache)\n cache.set(element, content);\n return content;\n}\nfunction parseCSSContentPropertyAsString(element, content, isPseudo) {\n if (!content || content === \"none\" || content === \"normal\") {\n return;\n }\n try {\n let tokens = tokenize(content).filter((token) => !(token instanceof WhitespaceToken));\n const delimIndex = tokens.findIndex((token) => token instanceof DelimToken && token.value === \"/\");\n if (delimIndex !== -1) {\n tokens = tokens.slice(delimIndex + 1);\n } else if (!isPseudo) {\n return;\n }\n const accumulated = [];\n let index = 0;\n while (index < tokens.length) {\n if (tokens[index] instanceof StringToken) {\n accumulated.push(tokens[index].value);\n index++;\n } else if (index + 2 < tokens.length && tokens[index] instanceof FunctionToken && tokens[index].value === \"attr\" && tokens[index + 1] instanceof IdentToken && tokens[index + 2] instanceof CloseParenToken) {\n const attrName = tokens[index + 1].value;\n accumulated.push(element.getAttribute(attrName) || \"\");\n index += 3;\n } else {\n return;\n }\n }\n return accumulated.join(\"\");\n } catch {\n }\n}\nfunction getAriaLabelledByElements(element) {\n const ref = element.getAttribute(\"aria-labelledby\");\n if (ref === null)\n return null;\n const refs = getIdRefs(element, ref);\n return refs.length ? refs : null;\n}\nfunction allowsNameFromContent(role, targetDescendant) {\n const alwaysAllowsNameFromContent = [\"button\", \"cell\", \"checkbox\", \"columnheader\", \"gridcell\", \"heading\", \"link\", \"menuitem\", \"menuitemcheckbox\", \"menuitemradio\", \"option\", \"radio\", \"row\", \"rowheader\", \"switch\", \"tab\", \"tooltip\", \"treeitem\"].includes(role);\n const descendantAllowsNameFromContent = targetDescendant && [\"\", \"caption\", \"code\", \"contentinfo\", \"definition\", \"deletion\", \"emphasis\", \"insertion\", \"list\", \"listitem\", \"mark\", \"none\", \"paragraph\", \"presentation\", \"region\", \"row\", \"rowgroup\", \"section\", \"strong\", \"subscript\", \"superscript\", \"table\", \"term\", \"time\"].includes(role);\n return alwaysAllowsNameFromContent || descendantAllowsNameFromContent;\n}\nfunction getElementAccessibleName(element, includeHidden) {\n const cache = includeHidden ? cacheAccessibleNameHidden : cacheAccessibleName;\n let accessibleName = cache == null ? void 0 : cache.get(element);\n if (accessibleName === void 0) {\n accessibleName = \"\";\n const elementProhibitsNaming = [\"caption\", \"code\", \"definition\", \"deletion\", \"emphasis\", \"generic\", \"insertion\", \"mark\", \"paragraph\", \"presentation\", \"strong\", \"subscript\", \"suggestion\", \"superscript\", \"term\", \"time\"].includes(getAriaRole(element) || \"\");\n if (!elementProhibitsNaming) {\n accessibleName = asFlatString(getTextAlternativeInternal(element, {\n includeHidden,\n visitedElements: /* @__PURE__ */ new Set(),\n embeddedInTargetElement: \"self\"\n }));\n }\n cache == null ? void 0 : cache.set(element, accessibleName);\n }\n return accessibleName;\n}\nfunction getTextAlternativeInternal(element, options) {\n var _a, _b, _c, _d;\n if (options.visitedElements.has(element))\n return \"\";\n const childOptions = {\n ...options,\n embeddedInTargetElement: options.embeddedInTargetElement === \"self\" ? \"descendant\" : options.embeddedInTargetElement\n };\n if (!options.includeHidden) {\n const isEmbeddedInHiddenReferenceTraversal = !!((_a = options.embeddedInLabelledBy) == null ? void 0 : _a.hidden) || !!((_b = options.embeddedInDescribedBy) == null ? void 0 : _b.hidden) || !!((_c = options.embeddedInNativeTextAlternative) == null ? void 0 : _c.hidden) || !!((_d = options.embeddedInLabel) == null ? void 0 : _d.hidden);\n if (isElementIgnoredForAria(element) || !isEmbeddedInHiddenReferenceTraversal && isElementHiddenForAria(element)) {\n options.visitedElements.add(element);\n return \"\";\n }\n }\n const labelledBy = getAriaLabelledByElements(element);\n if (!options.embeddedInLabelledBy) {\n const accessibleName = (labelledBy || []).map((ref) => getTextAlternativeInternal(ref, {\n ...options,\n embeddedInLabelledBy: { element: ref, hidden: isElementHiddenForAria(ref) },\n embeddedInDescribedBy: void 0,\n embeddedInTargetElement: void 0,\n embeddedInLabel: void 0,\n embeddedInNativeTextAlternative: void 0\n })).join(\" \");\n if (accessibleName)\n return accessibleName;\n }\n const role = getAriaRole(element) || \"\";\n const tagName = elementSafeTagName(element);\n if (!!options.embeddedInLabel || !!options.embeddedInLabelledBy || options.embeddedInTargetElement === \"descendant\") {\n const isOwnLabel = [...element.labels || []].includes(element);\n const isOwnLabelledBy = (labelledBy || []).includes(element);\n if (!isOwnLabel && !isOwnLabelledBy) {\n if (role === \"textbox\") {\n options.visitedElements.add(element);\n if (tagName === \"INPUT\" || tagName === \"TEXTAREA\")\n return element.value;\n return element.textContent || \"\";\n }\n if ([\"combobox\", \"listbox\"].includes(role)) {\n options.visitedElements.add(element);\n let selectedOptions;\n if (tagName === \"SELECT\") {\n selectedOptions = [...element.selectedOptions];\n if (!selectedOptions.length && element.options.length)\n selectedOptions.push(element.options[0]);\n } else {\n const listbox = role === \"combobox\" ? queryInAriaOwned(element, \"*\").find((e) => getAriaRole(e) === \"listbox\") : element;\n selectedOptions = listbox ? queryInAriaOwned(listbox, '[aria-selected=\"true\"]').filter((e) => getAriaRole(e) === \"option\") : [];\n }\n if (!selectedOptions.length && tagName === \"INPUT\") {\n return element.value;\n }\n return selectedOptions.map((option) => getTextAlternativeInternal(option, childOptions)).join(\" \");\n }\n if ([\"progressbar\", \"scrollbar\", \"slider\", \"spinbutton\", \"meter\"].includes(role)) {\n options.visitedElements.add(element);\n if (element.hasAttribute(\"aria-valuetext\"))\n return element.getAttribute(\"aria-valuetext\") || \"\";\n if (element.hasAttribute(\"aria-valuenow\"))\n return element.getAttribute(\"aria-valuenow\") || \"\";\n return element.getAttribute(\"value\") || \"\";\n }\n if ([\"menu\"].includes(role)) {\n options.visitedElements.add(element);\n return \"\";\n }\n }\n }\n const ariaLabel = element.getAttribute(\"aria-label\") || \"\";\n if (trimFlatString(ariaLabel)) {\n options.visitedElements.add(element);\n return ariaLabel;\n }\n if (![\"presentation\", \"none\"].includes(role)) {\n if (tagName === \"INPUT\" && [\"button\", \"submit\", \"reset\"].includes(element.type)) {\n options.visitedElements.add(element);\n const value = element.value || \"\";\n if (trimFlatString(value))\n return value;\n if (element.type === \"submit\")\n return \"Submit\";\n if (element.type === \"reset\")\n return \"Reset\";\n const title = element.getAttribute(\"title\") || \"\";\n return title;\n }\n if (tagName === \"INPUT\" && element.type === \"file\") {\n options.visitedElements.add(element);\n const labels = element.labels || [];\n if (labels.length && !options.embeddedInLabelledBy)\n return getAccessibleNameFromAssociatedLabels(labels, options);\n return \"Choose File\";\n }\n if (tagName === \"INPUT\" && element.type === \"image\") {\n options.visitedElements.add(element);\n const labels = element.labels || [];\n if (labels.length && !options.embeddedInLabelledBy)\n return getAccessibleNameFromAssociatedLabels(labels, options);\n const alt = element.getAttribute(\"alt\") || \"\";\n if (trimFlatString(alt))\n return alt;\n const title = element.getAttribute(\"title\") || \"\";\n if (trimFlatString(title))\n return title;\n return \"Submit\";\n }\n if (!labelledBy && tagName === \"BUTTON\") {\n options.visitedElements.add(element);\n const labels = element.labels || [];\n if (labels.length)\n return getAccessibleNameFromAssociatedLabels(labels, options);\n }\n if (!labelledBy && tagName === \"OUTPUT\") {\n options.visitedElements.add(element);\n const labels = element.labels || [];\n if (labels.length)\n return getAccessibleNameFromAssociatedLabels(labels, options);\n return element.getAttribute(\"title\") || \"\";\n }\n if (!labelledBy && (tagName === \"TEXTAREA\" || tagName === \"SELECT\" || tagName === \"INPUT\")) {\n options.visitedElements.add(element);\n const labels = element.labels || [];\n if (labels.length)\n return getAccessibleNameFromAssociatedLabels(labels, options);\n const usePlaceholder = tagName === \"INPUT\" && [\"text\", \"password\", \"search\", \"tel\", \"email\", \"url\"].includes(element.type) || tagName === \"TEXTAREA\";\n const placeholder = element.getAttribute(\"placeholder\") || \"\";\n const title = element.getAttribute(\"title\") || \"\";\n if (!usePlaceholder || title)\n return title;\n return placeholder;\n }\n if (!labelledBy && tagName === \"FIELDSET\") {\n options.visitedElements.add(element);\n for (let child = element.firstElementChild; child; child = child.nextElementSibling) {\n if (elementSafeTagName(child) === \"LEGEND\") {\n return getTextAlternativeInternal(child, {\n ...childOptions,\n embeddedInNativeTextAlternative: { element: child, hidden: isElementHiddenForAria(child) }\n });\n }\n }\n const title = element.getAttribute(\"title\") || \"\";\n return title;\n }\n if (!labelledBy && tagName === \"FIGURE\") {\n options.visitedElements.add(element);\n for (let child = element.firstElementChild; child; child = child.nextElementSibling) {\n if (elementSafeTagName(child) === \"FIGCAPTION\") {\n return getTextAlternativeInternal(child, {\n ...childOptions,\n embeddedInNativeTextAlternative: { element: child, hidden: isElementHiddenForAria(child) }\n });\n }\n }\n const title = element.getAttribute(\"title\") || \"\";\n return title;\n }\n if (tagName === \"IMG\") {\n options.visitedElements.add(element);\n const alt = element.getAttribute(\"alt\") || \"\";\n if (trimFlatString(alt))\n return alt;\n const title = element.getAttribute(\"title\") || \"\";\n return title;\n }\n if (tagName === \"TABLE\") {\n options.visitedElements.add(element);\n for (let child = element.firstElementChild; child; child = child.nextElementSibling) {\n if (elementSafeTagName(child) === \"CAPTION\") {\n return getTextAlternativeInternal(child, {\n ...childOptions,\n embeddedInNativeTextAlternative: { element: child, hidden: isElementHiddenForAria(child) }\n });\n }\n }\n const summary = element.getAttribute(\"summary\") || \"\";\n if (summary)\n return summary;\n }\n if (tagName === \"AREA\") {\n options.visitedElements.add(element);\n const alt = element.getAttribute(\"alt\") || \"\";\n if (trimFlatString(alt))\n return alt;\n const title = element.getAttribute(\"title\") || \"\";\n return title;\n }\n if (tagName === \"SVG\" || element.ownerSVGElement) {\n options.visitedElements.add(element);\n for (let child = element.firstElementChild; child; child = child.nextElementSibling) {\n if (elementSafeTagName(child) === \"TITLE\" && child.ownerSVGElement) {\n return getTextAlternativeInternal(child, {\n ...childOptions,\n embeddedInLabelledBy: { element: child, hidden: isElementHiddenForAria(child) }\n });\n }\n }\n }\n if (element.ownerSVGElement && tagName === \"A\") {\n const title = element.getAttribute(\"xlink:title\") || \"\";\n if (trimFlatString(title)) {\n options.visitedElements.add(element);\n return title;\n }\n }\n }\n const shouldNameFromContentForSummary = tagName === \"SUMMARY\" && ![\"presentation\", \"none\"].includes(role);\n if (allowsNameFromContent(role, options.embeddedInTargetElement === \"descendant\") || shouldNameFromContentForSummary || !!options.embeddedInLabelledBy || !!options.embeddedInDescribedBy || !!options.embeddedInLabel || !!options.embeddedInNativeTextAlternative) {\n options.visitedElements.add(element);\n const accessibleName = innerAccumulatedElementText(element, childOptions);\n const maybeTrimmedAccessibleName = options.embeddedInTargetElement === \"self\" ? trimFlatString(accessibleName) : accessibleName;\n if (maybeTrimmedAccessibleName)\n return accessibleName;\n }\n if (![\"presentation\", \"none\"].includes(role) || tagName === \"IFRAME\") {\n options.visitedElements.add(element);\n const title = element.getAttribute(\"title\") || \"\";\n if (trimFlatString(title))\n return title;\n }\n options.visitedElements.add(element);\n return \"\";\n}\nfunction innerAccumulatedElementText(element, options) {\n const tokens = [];\n const visit = (node, skipSlotted) => {\n var _a;\n if (skipSlotted && node.assignedSlot)\n return;\n if (node.nodeType === 1) {\n const display = ((_a = getElementComputedStyle(node)) == null ? void 0 : _a.display) || \"inline\";\n let token = getTextAlternativeInternal(node, options);\n if (display !== \"inline\" || node.nodeName === \"BR\")\n token = \" \" + token + \" \";\n tokens.push(token);\n } else if (node.nodeType === 3) {\n tokens.push(node.textContent || \"\");\n }\n };\n tokens.push(getCSSContent(element, \"::before\") || \"\");\n const content = getCSSContent(element);\n if (content !== void 0) {\n tokens.push(content);\n } else {\n const assignedNodes = element.nodeName === \"SLOT\" ? element.assignedNodes() : [];\n if (assignedNodes.length) {\n for (const child of assignedNodes)\n visit(child, false);\n } else {\n for (let child = element.firstChild; child; child = child.nextSibling)\n visit(child, true);\n if (element.shadowRoot) {\n for (let child = element.shadowRoot.firstChild; child; child = child.nextSibling)\n visit(child, true);\n }\n for (const owned of getIdRefs(element, element.getAttribute(\"aria-owns\")))\n visit(owned, true);\n }\n }\n tokens.push(getCSSContent(element, \"::after\") || \"\");\n return tokens.join(\"\");\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}\nfunction getAccessibleNameFromAssociatedLabels(labels, options) {\n return [...labels].map((label) => getTextAlternativeInternal(label, {\n ...options,\n embeddedInLabel: { element: label, hidden: isElementHiddenForAria(label) },\n embeddedInNativeTextAlternative: void 0,\n embeddedInLabelledBy: void 0,\n embeddedInDescribedBy: void 0,\n embeddedInTargetElement: void 0\n })).filter((accessibleName) => !!accessibleName).join(\" \");\n}\nvar cacheAccessibleName;\nvar cacheAccessibleNameHidden;\nvar cacheIsHidden;\nvar cachePseudoContent;\nvar cachePseudoContentBefore;\nvar cachePseudoContentAfter;\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/roleCategories.ts\nvar CONTROL_ROLES = /* @__PURE__ */ new Set([\"button\", \"link\", \"menuitem\", \"menuitemcheckbox\", \"menuitemradio\"]);\nvar CELL_ROLES = /* @__PURE__ */ new Set([\"cell\", \"gridcell\", \"columnheader\", \"rowheader\"]);\nvar CONTROL_CANDIDATES = \"button, input, a[href], area[href], [role]\";\nvar NATIVE_CONTROLS = 'button, a[href], input[type=\"button\"], input[type=\"submit\"], input[type=\"reset\"], input[type=\"image\"]';\nfunction isControl(element) {\n var _a;\n return element.matches(NATIVE_CONTROLS) || CONTROL_ROLES.has((_a = getAriaRole(element)) != null ? _a : \"\");\n}\nfunction isPopupTrigger(element) {\n const popup = element.getAttribute(\"aria-haspopup\");\n return popup !== null && popup !== \"false\" || element.hasAttribute(\"aria-expanded\");\n}\nfunction isTrigger(element) {\n return isControl(element) || isPopupTrigger(element);\n}\nfunction isCell(element) {\n var _a;\n return element.localName === \"td\" || element.localName === \"th\" || CELL_ROLES.has((_a = getAriaRole(element)) != null ? _a : \"\");\n}\nfunction closestControl(element) {\n for (let e = element; e; e = parentElementOrShadowHost(e)) {\n if (isControl(e))\n return e;\n }\n}\nfunction controlsIn(scope) {\n return [...scope.querySelectorAll(CONTROL_CANDIDATES)].filter(isControl);\n}\n\n// packages/injected/src/recorder/skyramp/ScopingHandler.ts\nvar LOG_PREFIX = \"[Scoping]\";\nvar SVG_NS = \"http://www.w3.org/2000/svg\";\nvar POSITIONAL_REASON = \"Positional selector (nth)\";\nfunction isPositionalSelector(selector) {\n return /(^|>> )nth=\\d+/.test(selector);\n}\nfunction isTitleOf(element, item) {\n for (let e = element; e && e !== item; e = e.parentElement) {\n if (e.matches(\"h1,h2,h3,h4,h5,h6,[role=heading]\") || [...e.classList].some((c) => /(^|[-_])(title|heading)([-_]|$)/i.test(c) || /[a-z](Title|Heading)([A-Z_-]|$)/.test(c)))\n return true;\n }\n return false;\n}\nfunction withoutPosition(selector) {\n return selector.split(\" >> \").filter((part) => !/^nth=\\d+$/.test(part)).join(\" >> \");\n}\nvar MAX_LAYOUT_SIBLINGS = 50;\nvar MAX_ANCHOR_COLUMNS = 4;\nfunction isNeverContainer(candidate, element) {\n return candidate.namespaceURI === SVG_NS || isIconHost(candidate, element);\n}\nfunction isIconHost(candidate, element) {\n const icon = svgRootOf(element);\n if (!icon || !candidate.contains(icon))\n return false;\n if (candidate.matches(\"button, a, input, select, [role=button], [role=link], [role=checkbox], [role=radio], [role=menuitem], [role=tab], [role=option], [role=switch]\"))\n return true;\n if ((candidate.textContent || \"\").trim())\n return false;\n for (let e = icon.parentElement; e; e = e.parentElement) {\n if (e.children.length !== 1)\n return false;\n if (e === candidate)\n return true;\n }\n return false;\n}\nvar bornClasses = /* @__PURE__ */ new WeakMap();\nvar gainedClasses = /* @__PURE__ */ new WeakMap();\nvar classObserver;\nfunction noteClassChanges(records) {\n var _a;\n for (const record of records) {\n if (record.type === \"childList\") {\n for (const node of record.addedNodes) {\n if (node.nodeType !== Node.ELEMENT_NODE)\n continue;\n for (const element2 of [node, ...node.querySelectorAll(\"[class]\")]) {\n if (!bornClasses.has(element2))\n bornClasses.set(element2, new Set(element2.classList));\n }\n }\n continue;\n }\n const element = record.target;\n const before = new Set(((_a = record.oldValue) != null ? _a : \"\").split(/\\s+/));\n for (const token of element.classList) {\n if (before.has(token))\n continue;\n let gained = gainedClasses.get(element);\n if (!gained)\n gainedClasses.set(element, gained = /* @__PURE__ */ new Set());\n gained.add(token);\n }\n }\n}\nfunction observeClassChanges(document2, recording) {\n if (!recording) {\n if (classObserver)\n noteClassChanges(classObserver.takeRecords());\n classObserver == null ? void 0 : classObserver.disconnect();\n classObserver = void 0;\n return;\n }\n if (classObserver)\n return;\n classObserver = new MutationObserver(noteClassChanges);\n classObserver.observe(document2, { subtree: true, childList: true, attributes: true, attributeFilter: [\"class\"], attributeOldValue: true });\n}\nfunction svgRootOf(element) {\n if (element.namespaceURI !== SVG_NS)\n return null;\n let root = element;\n for (let e = element; e && e.namespaceURI === SVG_NS; e = e.parentElement) {\n if (e.nodeName.toLowerCase() === \"svg\")\n root = e;\n }\n return root.nodeName.toLowerCase() === \"svg\" ? root : null;\n}\nfunction isElementVisuallyHidden(element, stopAt) {\n var _a, _b, _c, _d, _e;\n if (!isElementVisible(element))\n return true;\n const view = element.ownerDocument.defaultView;\n for (let e = element; e && e !== stopAt; e = e.parentElement) {\n const style = getElementComputedStyle(e);\n if (!style || style.display === \"contents\")\n continue;\n const box = e.getBoundingClientRect();\n if (box.width <= 1 && box.height <= 1)\n return true;\n const clip = style.clip.match(/rect\\(\\s*(-?[\\d.]+)px,?\\s*(-?[\\d.]+)px,?\\s*(-?[\\d.]+)px,?\\s*(-?[\\d.]+)px\\s*\\)/);\n if (clip && +clip[2] - +clip[4] <= 1 && +clip[3] - +clip[1] <= 1)\n return true;\n const inset = (_a = style.clipPath.match(/^inset\\(([^)]*)\\)/)) == null ? void 0 : _a[1].split(/\\s+round\\s+/)[0].trim().split(/\\s+/);\n if (inset) {\n const [top, right = top, bottom = top, left = right] = inset;\n const px = (v, size) => v.endsWith(\"%\") ? parseFloat(v) * size / 100 : parseFloat(v) || 0;\n if (box.height - px(top, box.height) - px(bottom, box.height) <= 1 || box.width - px(left, box.width) - px(right, box.width) <= 1)\n return true;\n }\n let scroller = e.parentElement;\n while (scroller && !/(auto|scroll|hidden|clip)/.test((_c = (_b = getElementComputedStyle(scroller)) == null ? void 0 : _b.overflow) != null ? _c : \"\"))\n scroller = scroller.parentElement;\n const origin = scroller ? scroller.getBoundingClientRect() : { left: -((_d = view == null ? void 0 : view.scrollX) != null ? _d : 0), top: -((_e = view == null ? void 0 : view.scrollY) != null ? _e : 0) };\n if (box.right - origin.left + (scroller ? scroller.scrollLeft : 0) <= 0 || box.bottom - origin.top + (scroller ? scroller.scrollTop : 0) <= 0)\n return true;\n }\n return false;\n}\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 MAX_SECTION_CANDIDATES = 50;\nvar MAX_SECTION_LEVELS = 8;\nvar SECTION_HEADINGS = \"h1,h2,h3,h4,h5,h6,[role=heading]\";\nvar SECTION_ROLES = /* @__PURE__ */ new Set([\"region\", \"group\", \"table\", \"grid\", \"form\", \"dialog\", \"tabpanel\"]);\nvar isHeadClass = (c) => /(^|[-_])(head|header|heading|title)([-_]|$)/i.test(c) || /[a-z](Head|Header|Heading|Title)([A-Z_-]|$)/.test(c);\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 // Other build-generated names: styled-jsx (jsx-123456), CSS modules\n // (Card_title__3xYz1: block_element__ plus a 5-character hash with a\n // digit), and short hashes with two or more digits (title-x7k2p9). BEM\n // names (card__title, modal__close) and names like icon-arrow2 are not.\n /^jsx-/i,\n /^[A-Za-z][A-Za-z0-9-]*_[A-Za-z0-9-]+__(?=[A-Za-z0-9_-]*\\d)[A-Za-z0-9_-]{5}$/,\n /^[a-zA-Z]+-(?=[a-z0-9]*\\d[a-z0-9]*\\d)[a-z0-9]{5,}$/,\n // Toggled states (SKYR-4549), also one the page set before the recorder\n // started: observeClassChanges sees only the classes gained after that.\n /^(active|open|collapsed|selected|in|show)$/\n];\nfunction isNonIdentifyingClass(name) {\n return CSS_SKIP_PATTERNS.some((pattern) => pattern.test(name));\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 const result = this._scope(element, selector, elements);\n if (result && elements.length === 1) {\n const verification = this._verifySelector(result.selector);\n const [found] = verification.elements;\n if (!verification.valid || !(found === elements[0] || found.contains(elements[0]) || elements[0].contains(found))) {\n log(\"Scoped selector does not resolve to the original element alone, keeping original\");\n return null;\n }\n }\n return result;\n }\n _scope(element, selector, elements) {\n var _a;\n element = (_a = svgRootOf(element)) != null ? _a : element;\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 const section = this._anchorToAncestor(element, null, \"\", selector, elements, false);\n logGroupEnd();\n if (section) {\n log(\"Using section-anchored selector:\", section);\n const verification2 = this._verifySelector(section);\n return { selector: section, container: null, elements: verification2.elements, strategy: \"alternative\", description: section, containerSelector: \"\", containerIndex: -1, relativeSelector: \"\", usesTextFilter: true };\n }\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 preFixScoped = needsScoping.reason !== POSITIONAL_REASON;\n const anchored = this._anchorToAncestor(element, container, relativeSelector, selector, elements, preFixScoped);\n if (anchored) {\n scopedSelector = anchored;\n usesTextFilter = true;\n log(\"Using anchored selector:\", scopedSelector);\n } else if (needsScoping.reason === POSITIONAL_REASON || this._isStableOriginal(container.element, selector, elements)) {\n log(\"No content anchor and the original is no worse than nth, keeping original\");\n logGroupEnd();\n return null;\n } else {\n scopedSelector = `${container.selector} >> nth=${containerIndex} >> ${relativeSelector}`;\n log(\"Using nth-based selector:\", scopedSelector);\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 if (isPositionalSelector(selector)) {\n return { needed: true, reason: POSITIONAL_REASON };\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, kinds) {\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[2]) || shortHexMatch[2].length >= 4 && (kinds == null ? void 0 : kinds.includes(shortHexMatch[1].toLowerCase())))) return true;\n const counterMatch = id.match(/^(.*?[a-zA-Z])([0-9]+)$/);\n if (counterMatch && (counterMatch[2].length >= 3 || (kinds == null ? void 0 : kinds.includes(counterMatch[1].toLowerCase())))) return true;\n if (id.includes(\":\")) return true;\n if (/«r[0-9a-z]*»|(^|[-_])_?r_[0-9a-z]+_/.test(id)) 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 const kinds = [element.localName, getAriaRole(element)].filter((kind) => !!kind);\n if (this._isDynamicId(id, kinds)) {\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 if ([...selector.replace(/\"(?:[^\"\\\\]|\\\\.)*\"/g, '\"\"').matchAll(/\\.((?:\\\\.|[\\w-])+)/g)].some(([, name]) => isNonIdentifyingClass(name))) {\n log(\"MATCHED: class that does not identify the element\");\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 if (isNeverContainer(current, element)) {\n current = current.parentElement;\n continue;\n }\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 var _a;\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 if (isNeverContainer(current, element)) {\n current = current.parentElement;\n continue;\n }\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) && ((_a = current.parentElement) == null ? void 0 : _a.getAttribute(componentInfo.attr)) !== componentInfo.name) {\n let selector = `[${componentInfo.attr}=${this._quoteCSSAttributeValue(componentInfo.name)}]`;\n const outermost = `${selector}:not(${selector} *)`;\n if (current.querySelector(selector) && current.matches(outermost))\n selector = outermost;\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 * SKYR-4299 / SKYR-4549: the one ancestor climb, the only entry point of\n * anchoring (with or without a repeated item). Anchors the element to an\n * enclosing element's identifying content, one level at a time: the\n * repeated item (`container`: a row, card or option, when there is one),\n * then the row a cell container sits in, then the named section around it\n * (_anchorToSection, which starts above the item). Each level yields\n * candidate filters, most stable first, lazily, and one is taken only if\n * it resolves to exactly that level's element (_resolvesTo). Returns the\n * full selector, or null when no level names the element; the caller then\n * keeps the original or falls back to nth.\n *\n * One gate, _mayReanchor: the strategies this re-fix adds (the column\n * rule, a test id or a cell text an aria-label quotes; the section level\n * without the pre-fix widening) run only where the\n * pre-fix scoping would have recorded a position. They come after the\n * pre-fix strategies (a row's link, its text filter, the enclosing row's\n * label or row header), which run as before: where those named the item\n * the recording is exactly what it was.\n *\n * `preFixScoped` is set when the scoping before the re-fix scoped this\n * selector at all (any _needsScoping reason but the positional Rule 6);\n * only then is its text filter tried.\n */\n _anchorToAncestor(element, container, relative, selector, elements, preFixScoped) {\n var _a;\n const open = this._mayReanchor(selector, elements, preFixScoped);\n if (container) {\n for (const filter of this._getItemFilters(container.element, container.selector, preFixScoped, open, withoutPosition(selector))) {\n if (this._resolvesTo(filter, container.element))\n return `${filter} >> ${relative}`;\n }\n const row = this._enclosingRow(container.element);\n if (row) {\n const rowRelative = this._getRowRelativeSelector(row, element);\n for (const filter of rowRelative ? this._getRowFilters(row) : []) {\n if (this._resolvesTo(filter, row))\n return `${filter} >> ${rowRelative}`;\n }\n }\n }\n if (!this._mayReanchor(selector, elements, false))\n return null;\n const target = (_a = elements.find((e) => e === element || e.contains(element))) != null ? _a : element;\n return this._anchorToSection(target, withoutPosition(selector), container == null ? void 0 : container.element);\n }\n /**\n * The gate of the climb: whether a strategy the pre-fix scoping did not\n * have may replace the selector. Yes for a selector that is positional or\n * not unique. Yes also where the pre-fix scoping scoped it (`preFixScoped`,\n * _needsScoping Rules 2-5, e.g. a unique button in a multi-row table):\n * the climb tries the pre-fix strategies first, so a new one only runs\n * where those found nothing and the pre-fix scoping recorded\n * `container >> nth=N`, a position. A unique selector the pre-fix scoping\n * left alone is never replaced.\n */\n _mayReanchor(selector, elements, preFixScoped) {\n return preFixScoped || elements.length > 1 || isPositionalSelector(selector);\n }\n /** Whether `selector` resolves to exactly `element`. */\n _resolvesTo(selector, element) {\n const verification = this._verifySelector(selector);\n return verification.valid && verification.elements[0] === element;\n }\n /**\n * Whether `element` is a table-like row (the containers row strategies\n * apply to): by its ARIA role, explicit (role=row) or implicit (<tr>). A\n * design system's row class adds nothing: Cisco Magnetic's\n * `a-data-table__row` is rendered on `<tr role=\"row\">`. A role-less div\n * row is a repeated item like a card (its own text, its labels, columns).\n */\n _isRowContainer(element) {\n return element.getAttribute(\"role\") === \"row\" || element.tagName === \"TR\";\n }\n /**\n * The candidate filters of a repeated item, most stable first, each as\n * `<container> >> <filter>[ >> <filter>…]`, generated lazily so the first\n * that resolves ends the work:\n * 1. a row's link, by its exact accessible name (has-filter);\n * 2. the pre-fix text filter (only when the pre-fix scoping scoped this\n * selector): its first acceptable identifying text, `has-text`;\n * 3. behind the gate (`open`, see _anchorToAncestor): the column rule\n * (_getColumnFilters), then a test id or a cell text an aria-label\n * quotes (_labelledCellTexts).\n *\n * An item that is not a row (a card, a widget; its own stable id or test\n * id the generator already chains): its text (a title, a greeting, a\n * label in the page's locale) can change between recording and replay,\n * while the generator's position (`… >> nth=N`) shifts only when an\n * earlier element gains or loses a match of `base` (the generator's\n * selector without its positions). So behind the gate its text names it\n * only where _textMayName allows; otherwise the generator's selector\n * stays.\n */\n *_getItemFilters(container, containerSelector, preFixScoped, open, base) {\n if (this._isRowContainer(container)) {\n for (const link of container.querySelectorAll(\"a\")) {\n const text = this._clean(link.textContent);\n if (this._isAnchorText(text, true))\n yield `${containerSelector} >> internal:has=${JSON.stringify(`internal:role=link[name=\"${this._escapeTextFilter(text)}\"s]`)}`;\n }\n }\n const sources = this._getTextSources(container);\n const card = !this._isRowContainer(container);\n if (preFixScoped) {\n const legacy = sources.find((s) => s.use !== \"columns\" && s.text.length !== 3 && this._isAnchorText(s.text, s.allowDynamic));\n if (legacy && card && this._hasVolatileText(legacy.text)) {\n yield* this._datedCardTitle(container, containerSelector, sources, legacy);\n return;\n }\n if (legacy)\n yield `${containerSelector} >> ${this._looseFilter(legacy.text)}`;\n }\n if (!open)\n return;\n const containerSelectors = [containerSelector];\n const itemClass = containerSelector === container.tagName.toLowerCase() ? this._getItemClass(container) : null;\n if (itemClass)\n containerSelectors.push(`${containerSelector}.${this._escapeCSS(itemClass)}`);\n const groups = [...this._getColumnFilters(container, sources), ...sources.filter((s) => !s.el && this._isAnchorText(s.text, s.allowDynamic)).map((s) => [s.text])];\n const ofKind = (peer) => peer.matches(containerSelector) && (!itemClass || peer.classList.contains(itemClass));\n for (const group of groups) {\n if (card && !this._textMayName(container, base, group, ofKind))\n return;\n for (const filter of [group.map((t) => this._exactFilter(t)), group.map((t) => this._looseFilter(t))]) {\n for (const sel of containerSelectors)\n yield [sel, ...filter].join(\" >> \");\n }\n }\n }\n /**\n * SKYR-4299: the filters naming a card whose first text has a date or time\n * (`dated`) by its title instead: the first later text the pre-fix filter\n * could take (_getItemFilters) that has no date, time or other digit (a\n * count: \"Open tasks (4)\"), is the card's title (in a heading, or an\n * element whose class says title or heading), is held by no other card of\n * its kind, and differs in every card\n * of its kind that lines up with it (_getColumnValues). A plain text that\n * passes the rest can be a status (\"Running\" on one deploy card of three)\n * that names another card once it changes, and a status that repeats\n * (\"Closed\" on two cards) is no name. None: the card keeps its position.\n */\n *_datedCardTitle(container, containerSelector, sources, dated) {\n var _a, _b;\n const itemClass = containerSelector === container.tagName.toLowerCase() ? this._getItemClass(container) : null;\n const peers = Array.from((_b = (_a = container.parentElement) == null ? void 0 : _a.children) != null ? _b : []).filter((c) => c !== container && c.matches(containerSelector) && (!itemClass || c.classList.contains(itemClass))).slice(0, MAX_LAYOUT_SIBLINGS);\n const peerTexts = peers.map((peer) => this._clean(peer.textContent).toLowerCase());\n for (const source of sources.slice(sources.indexOf(dated) + 1)) {\n if (source.use === \"columns\" || !source.el || source.text.length === 3 || !this._isAnchorText(source.text, source.allowDynamic) || this._hasVolatileText(source.text) || /\\p{Nd}/u.test(source.text) || !isTitleOf(source.el, container))\n continue;\n if (peerTexts.some((text) => text.includes(source.text.toLowerCase())))\n continue;\n const values = this._getColumnValues(source, container, peers);\n if (values.length < 2 || new Set(values).size !== values.length)\n continue;\n yield `${containerSelector} >> ${this._looseFilter(source.text)}`;\n if (itemClass)\n yield `${containerSelector}.${this._escapeCSS(itemClass)} >> ${this._looseFilter(source.text)}`;\n return;\n }\n }\n /**\n * Whether `texts` may name `item` (a card, a section) instead of the\n * generator's position. A text can change between recording and replay;\n * the position (`… >> nth=N`, `base` without it) shifts only when an\n * earlier element gains or loses a match of `base`. So: only when an\n * earlier sibling of `item` of its kind holds no match of\n * `base` (it can gain one at replay, as a section that gets rows), and\n * only when no text has a digit (a count or a record number: \"Details\n * (4)\", \"Order 1042\"). `ofKind` says which siblings are of its kind.\n */\n _textMayName(item, base, texts, ofKind) {\n if (texts.some((t) => /\\p{Nd}/u.test(t)))\n return false;\n const matches = this._verifySelector(base).elements;\n for (let peer = item.previousElementSibling; peer; peer = peer.previousElementSibling) {\n if (ofKind(peer) && !matches.some((m) => peer.contains(m)))\n return true;\n }\n return false;\n }\n /**\n * SKYR-4549: the classes of `element` that say what it is: not a utility,\n * build-generated or state class (isNonIdentifyingClass), not one that\n * says where it is (a digit, as in `block-2`, or first, last, odd, even),\n * and not a state (bornClasses, gainedClasses): not one it gained while\n * the page ran, and, when it was in the page before the recorder\n * started, only one another element carries without having gained it,\n * outside its own ancestors and\n * descendants (a state the page set under a resting pointer is on the\n * hovered chain, or on elements that gained it since).\n */\n _kindClasses(element) {\n var _a;\n noteClassChanges((_a = classObserver == null ? void 0 : classObserver.takeRecords()) != null ? _a : []);\n const born = bornClasses.get(element);\n const gained = gainedClasses.get(element);\n return [...element.classList].filter((c) => !isNonIdentifyingClass(c) && !/\\p{Nd}|(^|[-_])(first|last|odd|even)$/iu.test(c) && !(gained == null ? void 0 : gained.has(c)) && (born ? born.has(c) : [...element.ownerDocument.getElementsByClassName(c)].some((o) => {\n var _a2;\n return !o.contains(element) && !element.contains(o) && !((_a2 = gainedClasses.get(o)) == null ? void 0 : _a2.has(c));\n })));\n }\n /** `internal:has=\"internal:text=\\\"X\\\"s\"`: an element whose whole text is X. */\n _exactFilter(text) {\n return `internal:has=${JSON.stringify(`internal:text=\"${this._escapeTextFilter(text)}\"s`)}`;\n }\n /** `internal:has-text=\"X\"i`: an element whose text contains X. */\n _looseFilter(text) {\n return `internal:has-text=\"${this._escapeTextFilter(text)}\"i`;\n }\n /**\n * The column rule: the texts that tell this item apart from its\n * same-layout siblings, as groups to try in order (shortest first).\n *\n * Each candidate text is a column: its value here and at the same place in\n * every sibling with the item's layout (_getColumnValues). A column is\n * eligible when it lines up with at least one sibling, is visible, is not\n * a control's label (an action, not the item's name) and is not data: most\n * of its values volatile (isVolatileValue), or a count with one unit word\n * (\"12 views\", \"40 views\"). Eligible columns are ranked by (a dynamic id\n * only as a last resort, all values distinct, document order), and the\n * groups are the prefixes of that ranking, from the shortest that holds\n * two all-distinct columns (when the item has two: then a change in either\n * gives 0 matches at replay, never another item; one alone can be a status\n * that moves to another row) up to MAX_ANCHOR_COLUMNS columns. The caller\n * takes the first group that is unique.\n */\n _getColumnFilters(container, sources) {\n var _a, _b;\n const siblings = Array.from((_b = (_a = container.parentElement) == null ? void 0 : _a.children) != null ? _b : []).filter((c) => c !== container && c.tagName === container.tagName).slice(0, MAX_LAYOUT_SIBLINGS);\n const columns = [];\n const seen = /* @__PURE__ */ new Set();\n for (const source of sources) {\n if (source.use === \"legacy\" || !source.el || seen.has(source.text) || !this._isAnchorText(source.text, source.allowDynamic))\n continue;\n seen.add(source.text);\n const values = this._getColumnValues(source, container, siblings);\n if (values.length < 2 || isElementVisuallyHidden(source.el, container) || this._isControlLabel(source.el, container) || this._isDataColumn(values))\n continue;\n columns.push({ text: source.text, el: source.el, pass: source.allowDynamic && this._isDynamic(source.text) ? 1 : 0, all: new Set(values).size === values.length });\n }\n columns.sort((a, b) => a.pass - b.pass || Number(b.all) - Number(a.all) || (a.el === b.el ? 0 : a.el.compareDocumentPosition(b.el) & Node.DOCUMENT_POSITION_FOLLOWING ? -1 : 1));\n const first = Math.max(1, Math.min(2, columns.filter((c) => c.all && !c.pass).length));\n const groups = [];\n for (let n = first; n <= Math.min(columns.length, MAX_ANCHOR_COLUMNS); n++)\n groups.push(columns.slice(0, n).map((c) => c.text));\n return groups;\n }\n /** Whether `element` is (in) a control inside `item` (closestControl): its text is an action, not the item's name. */\n _isControlLabel(element, item) {\n const control = closestControl(element);\n return !!control && control !== item && item.contains(control);\n }\n /** Whether a column is data (see _getColumnFilters). */\n _isDataColumn(values) {\n const options = this._volatileOptions();\n if (values.filter((v) => isVolatileValue(v, options)).length * 2 > values.length)\n return true;\n const units = values.map((v) => {\n var _a;\n return (_a = v.match(/^[+\\-−]?\\d[\\d.,]*\\s*(\\p{L}[\\p{L} ]*)$/u)) == null ? void 0 : _a[1].toLowerCase();\n });\n return units.every((u) => u && u === units[0]);\n }\n /**\n * Whether the generator's selector is unique, not positional, not dynamic,\n * and does not embed a button name of `container` that is not the same in\n * every same-layout sibling (its state: \"Approve\" vs \"Revoke\"), so it may\n * stand instead of a positional fallback. A selector of an element around\n * the container names no item: the generator retargeted the click to a\n * wrapper of the whole list (SKYR-5064: SMFL Financial's page-wide\n * dropzone `role=button`), and replay would click the wrapper's centre.\n */\n _isStableOriginal(container, selector, elements) {\n var _a, _b;\n if (elements.length !== 1 || isPositionalSelector(selector) || this._hasDynamicSelector(selector))\n return false;\n if (elements[0] !== container && elements[0].contains(container))\n return false;\n const siblings = Array.from((_b = (_a = container.parentElement) == null ? void 0 : _a.children) != null ? _b : []).filter((c) => c !== container && c.tagName === container.tagName).slice(0, MAX_LAYOUT_SIBLINGS);\n const buttonName = (e) => getElementAccessibleName(e, false);\n const unstable = Array.from(container.querySelectorAll('button, [role=\"button\"], input[type=\"button\"], input[type=\"submit\"], input[type=\"reset\"]')).map((button) => {\n const name = buttonName(button);\n const values = this._getColumnValues({ el: button, text: name, kind: \"full\", allowDynamic: true, use: \"columns\" }, container, siblings, buttonName);\n return name && !(values.length > 1 && new Set(values).size === 1) ? name : \"\";\n }).filter(Boolean);\n return !unstable.some((text) => new RegExp(`(?<![\\\\p{L}\\\\p{N}])${escapeRegExp(text)}(?![\\\\p{L}\\\\p{N}])`, \"iu\").test(selector));\n }\n /** The class that marks `element` as a repeating item (see _findRepeatingContainerByClass). */\n _getItemClass(element) {\n var _a;\n const shared = (cls) => ((n) => n > 1 && n < 100)(element.ownerDocument.querySelectorAll(`.${this._escapeCSS(cls)}`).length);\n return (_a = Array.from(element.classList).find((cls) => !CSS_SKIP_PATTERNS.some((p) => p.test(cls)) && CSS_ITEM_PATTERNS.some((p) => p.test(cls)) && shared(cls))) != null ? _a : null;\n }\n /**\n * The values in the column of `source`: its text, and the text at the same\n * place in each sibling container with the item's layout — the same\n * number of children at every step and the same child position (classes\n * ignored: a hover or \"unread\" class on the clicked row's cells does not\n * matter). Siblings of another layout (a colspan or rowspan) do not count.\n */\n _getColumnValues(source, container, siblings, read) {\n const path = [];\n for (let e = source.el; e !== container && e.parentElement; e = e.parentElement)\n path.unshift({ index: Array.from(e.parentElement.children).indexOf(e), count: e.parentElement.children.length });\n const values = [source.text];\n for (const sibling of siblings) {\n let e = sibling;\n for (const step of path)\n e = e && e.children.length === step.count ? e.children[step.index] : void 0;\n const text = e && this._clean(read ? read(e) : source.kind === \"full\" ? e.textContent : this._getDirectTextContent(e));\n if (text)\n values.push(text);\n }\n return values;\n }\n _clean(text) {\n return (text || \"\").replace(/\\s+/g, \" \").trim();\n }\n /**\n * Whether `text` may identify an element: long enough, not generic, not a\n * dynamic id unless `allowDynamic`. Whether it is data is judged by\n * isVolatileValue (for a column, over the whole column).\n */\n _isAnchorText(text, allowDynamic) {\n if (text.length < 2 || text.length > 100 || this._isGenericText(text) || text.includes(\">>\"))\n return false;\n return allowDynamic || !this._isDynamic(text);\n }\n /** Whether a name may anchor: identifying text (_isAnchorText) that is not data (isVolatileValue). */\n _isStableName(name) {\n return !!name && this._isAnchorText(name, false) && !isVolatileValue(name, this._volatileOptions());\n }\n /** The page-safe Intl and the page's languages, for isVolatileValue. */\n _volatileOptions() {\n return { intl: this._injectedScript.utils.builtins.Intl, locales: [...navigator.languages || [], this._injectedScript.document.documentElement.lang] };\n }\n /**\n * The candidate identifying texts of a repeated item, in priority order,\n * one list for both readers: `use` says whether the pre-fix text filter\n * (`legacy`), the column rule (`columns`) or both read a source.\n *\n * A row (_isRowContainer), in two passes (SKYR-3706: the first excludes\n * dynamic-looking text such as UUIDs and 6+ digit ids, the second allows\n * them as a last resort, so a backend id \"99925484\" in the first cell is\n * not picked over the workflow name \"WF1\" in a later one): link text,\n * cell text, then (columns only) text nested in the cells. Then a test id\n * that names the item and each cell text an aria-label in the row quotes\n * (_labelledCellTexts: \"Open actions menu for X\", in any language).\n *\n * Any other item (options, list items, cards): its own text, its\n * aria-label, the text of its spans, divs, paragraphs and headings, the\n * aria-labels inside it, then its whole text (legacy only) and the text of\n * any other element (columns only).\n */\n _getTextSources(container) {\n const sources = [];\n const add = (el, text, kind, allowDynamic, use = \"both\") => sources.push({ el, text: this._clean(text), kind, allowDynamic, use });\n if (this._isRowContainer(container)) {\n for (const allowDynamic of [false, true]) {\n for (const link of container.querySelectorAll(\"a\"))\n add(link, link.textContent, \"full\", allowDynamic);\n for (const cell of container.querySelectorAll('[role=\"cell\"], [role=\"gridcell\"], td'))\n add(cell, this._getDirectTextContent(cell), \"direct\", allowDynamic);\n for (const el of container.querySelectorAll(\"*\"))\n add(el, this._getDirectTextContent(el), \"direct\", allowDynamic, \"columns\");\n }\n const testIdElement = container.querySelector(\"[data-testid], [data-test-id]\");\n const testId = testIdElement && this._getTestId(testIdElement);\n if (testId && !this._isDynamic(testId) && !/^(item|row|cell|grid)/i.test(testId))\n add(null, testId, \"aria\", true);\n for (const name of this._labelledCellTexts(container))\n add(null, name, \"aria\", true);\n return sources;\n }\n add(container, this._getDirectTextContent(container), \"direct\", true);\n add(null, container.getAttribute(\"aria-label\"), \"aria\", true);\n for (const el of container.querySelectorAll(\"span, div, p, h1, h2, h3, h4, h5, h6\"))\n add(el, this._getDirectTextContent(el), \"direct\", true);\n for (const el of container.querySelectorAll(\"[aria-label]\"))\n add(null, el.getAttribute(\"aria-label\"), \"aria\", true);\n add(container, container.textContent, \"full\", true, \"legacy\");\n for (const el of container.querySelectorAll(\"*\"))\n add(el, this._getDirectTextContent(el), \"direct\", true, \"columns\");\n return sources;\n }\n /**\n * The texts of `row` that one of its aria-labels quotes, longest first: an\n * aria-label in the row that contains another cell's text names the item\n * by it (\"Open actions menu for NK VPN Profile\", \"NK VPN Profileの操作メニュー\"),\n * in any language. A text is a cell's whole text or an element's own text\n * (a name button sharing its cell with the menu button, a name next to a\n * \"Default\" badge); the labelled element's own subtree and the cell it sits\n * in do not count, nor does a text that cannot anchor (_isAnchorText) or\n * is data (isVolatileValue: \"Download invoice 2026-10-21\"). The quote must\n * not cut a word of a script that spaces its words (\"VP\" is not quoted by\n * \"VPN\"). Longest first, so the name usually wins over a status a label\n * also quotes (\"Mark as Active\"); a heuristic, not a rule. Unlike the\n * English \"menu for X\" capture it replaces, a name must be some element's\n * text, and any aria-label may quote it.\n */\n _labelledCellTexts(row) {\n const candidates = [];\n for (const cell of row.querySelectorAll('[role=\"cell\"], [role=\"gridcell\"], td'))\n candidates.push({ el: cell, text: this._clean(cell.textContent) });\n for (const el of row.querySelectorAll(\"*\"))\n candidates.push({ el, text: this._clean(this._getDirectTextContent(el)) });\n const names = /* @__PURE__ */ new Set();\n const volatileOptions = this._volatileOptions();\n for (const labelled of row.querySelectorAll(\"[aria-label]\")) {\n const label = this._clean(labelled.getAttribute(\"aria-label\"));\n for (const { el, text } of candidates) {\n const own = labelled.contains(el) || el.contains(labelled) && el.matches('[role=\"cell\"], [role=\"gridcell\"], td');\n if (!own && !names.has(text) && this._isAnchorText(text, true) && quotes(label, text) && !isVolatileValue(text, volatileOptions))\n names.add(text);\n }\n }\n return [...names].sort((a, b) => b.length - a.length);\n }\n /** The role=row or <tr> strictly above `container`, or null. */\n _enclosingRow(container) {\n for (let row = container.parentElement; row && row !== container.ownerDocument.body; row = row.parentElement) {\n if (row.getAttribute(\"role\") === \"row\" || row.tagName === \"TR\")\n return row;\n }\n return null;\n }\n /**\n * The target's selector inside its row: the plain role form\n * (getByRole('radio')) when it is unique in the row, else the relative\n * selector.\n */\n _getRowRelativeSelector(row, target) {\n const role = this._getRole(target);\n if (role) {\n const roleSel = `internal:role=${role}`;\n try {\n if (this._injectedScript.querySelectorAll(this._injectedScript.parseSelector(roleSel), row).length === 1)\n return roleSel;\n } catch {\n }\n }\n return this._generateRelativeSelector(row, target) || null;\n }\n /**\n * The candidate filters of the row a container (a gridcell holding only a\n * label-less radio) sits in: the row's own label (aria-label /\n * aria-labelledby) as `internal:role=row[name=…]`, then its row header's\n * accessible name as a has-filter (a content-derived row name concatenates\n * volatile cells such as a modified date). Each exact (\"s\") first, so a\n * sibling row whose name merely contains this one (\"skyramp\" vs \"Skyramp\n * Testing\") cannot collide, then substring (\"i\"): a text-derived name\n * misses hidden decorations in the computed one (Box prefixes a \"Personal\n * folder\" icon label).\n */\n _getRowFilters(row) {\n var _a, _b;\n const filters = [];\n let explicitName = row.getAttribute(\"aria-label\");\n const labelledBy = row.getAttribute(\"aria-labelledby\");\n if (!explicitName && labelledBy)\n explicitName = ((_b = (_a = row.ownerDocument.getElementById(labelledBy)) == null ? void 0 : _a.textContent) == null ? void 0 : _b.trim()) || null;\n if (this._isStableName(explicitName)) {\n for (const flag of [\"s\", \"i\"])\n filters.push(`internal:role=row[name=${this._quoteCSSAttributeValue(explicitName)}${flag}]`);\n }\n const rowheader = row.querySelector('[role=\"rowheader\"], th');\n const headerName = rowheader && this._getAccessibleName(rowheader);\n if (this._isStableName(headerName)) {\n const rowSelector = row.tagName === \"TR\" ? \"tr\" : '[role=\"row\"]';\n for (const flag of [\"s\", \"i\"])\n filters.push(`${rowSelector} >> internal:has=${JSON.stringify(`internal:role=rowheader[name=${this._quoteCSSAttributeValue(headerName)}${flag}]`)}`);\n }\n return filters;\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 // SKYR-4299: literal dots; `...` matched every 3-character name (\"Bob\").\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 const icons = td.querySelectorAll(\"svg\");\n if (icons.length === 1 && !(td.textContent || \"\").trim() && (target === icons[0] || target.contains(icons[0]))) {\n log(\"Strategy 5b: lone icon in table cell ->\", `td >> nth=${cellIndex} >> svg`);\n return `td >> nth=${cellIndex} >> svg`;\n }\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 (this._hasHashedSegment(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 /**\n * A class name with a build hash in it: a `-`/`_`-separated segment of hex\n * with a digit and a letter, the hash `_isDynamicId` and `_isDynamic` know\n * as an id suffix (`textReset--6a276-EUA`, `card_a3f2b1`). Pure numbers are\n * design tokens in class names (`col-6`, `w-100`), not hashes.\n */\n _hasHashedSegment(cls) {\n return cls.split(/[-_]+/).some((segment) => /^[0-9a-f]{3,}$/i.test(segment) && /[0-9]/.test(segment) && /[a-f]/i.test(segment));\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 /**\n * SKYR-4549: the section level of the climb (_anchorToAncestor, behind its\n * gate). Anchors `base` (the generator's selector without its positions,\n * withoutPosition) to the innermost enclosing section that has a name, so\n * it stays on its element when a sibling section later renders the same\n * element (the pencil of a detail page's \"Details\" block once \"Schedule\"\n * gets rows too).\n *\n * A section is an ancestor above `item` (the repeated item, whose own\n * heading the column rule judges) with a name (_getSectionFilter), at most\n * MAX_SECTION_LEVELS ancestors up; a level without a name is skipped. Its name must not also name\n * another element's section (a twin heading elsewhere inside it). A named\n * section that neither its identity nor a safe name can address keeps\n * the generator's selector (no outer section is tried). The\n * anchored selector must match the element, alone or, when several\n * matches sit in the element's own table or list in that section, by its\n * position among them, so the locator still names the asserted row.\n */\n _anchorToSection(element, base, item) {\n var _a;\n const ownRows = (_a = element.closest(\"tr,li\")) == null ? void 0 : _a.parentElement;\n let innermost = true;\n let levels = 0;\n for (let section = (item != null ? item : element).parentElement; section && section !== this._injectedScript.document.body && levels++ < MAX_SECTION_LEVELS; section = section.parentElement) {\n const named = this._getSectionFilter(section, element, base);\n if (!named)\n continue;\n if (!named.filter)\n return null;\n const candidate = `${named.filter} >> ${base}`;\n const found = this._verifySelector(candidate).elements;\n const index = found.indexOf(element);\n if (index < 0) {\n return null;\n }\n if (found.length === 1)\n return candidate;\n if (innermost && ownRows && found.every((f) => {\n var _a2;\n return ((_a2 = f.closest(\"tr,li\")) == null ? void 0 : _a2.parentElement) === ownRows;\n }))\n return `${candidate} >> nth=${index}`;\n innermost = false;\n }\n return null;\n }\n /**\n * A section's filter, resolving to exactly that element, or null when it\n * has no usable name ({ filter: null }: it has one, but nothing may\n * address it). The name is, in order:\n * - its own accessible name, for a named region, group, table, grid, form,\n * dialog or tab panel (`<section aria-label=\"Tasks\">`):\n * `internal:role=region[name=\"Tasks\"s]`;\n * - a child heading (getAriaRole 'heading': h1-h6, role=heading) that comes\n * before the element's block: `<css> >> internal:has=\"internal:role=heading[name=\\\"X\\\"s]\"`;\n * - last resort, a first child that is a header, legend or caption, or\n * carries a header class token (`div.panel-heading > span`), before the\n * element's block: `<css> >> internal:has=\"internal:text=\\\"X\\\"s\"`.\n * The name must be stable text (_isStableName) and not also be another\n * heading's text inside the section. The css is the tag and its first\n * stable class, else all its stable classes (_kindClasses); a css\n * matching more than MAX_SECTION_CANDIDATES elements is too common to scan\n * on every hover.\n *\n * A name can change between recording and replay (a greeting, the user's\n * name, the locale, a count), the section's identity does not. So a named\n * section is addressed by, in order: its own stable id or test id\n * (_stableIdFilter); its css, when that alone resolves to it; its name,\n * where _textMayName allows (an earlier section of its kind lacks `base`,\n * and the name has no digit). Otherwise the generator's selector stays.\n */\n _getSectionFilter(section, element, base) {\n var _a;\n let csss;\n const sectionCss = () => {\n if (!csss) {\n const tag = section.tagName.toLowerCase();\n const classes = this._kindClasses(section).map((c) => \".\" + escapeCSS(c));\n csss = [.../* @__PURE__ */ new Set([tag + classes.slice(0, 1).join(\"\"), tag + classes.join(\"\")])].filter((css) => this._injectedScript.document.querySelectorAll(css).length <= MAX_SECTION_CANDIDATES);\n }\n return csss;\n };\n const byIdentity = () => {\n var _a2;\n return (_a2 = this._stableIdFilter(section)) != null ? _a2 : sectionCss().find((css) => this._resolvesTo(css, section));\n };\n const [kind] = this._kindClasses(section);\n const ofKind = (peer) => peer.tagName === section.tagName && (kind ? peer.classList.contains(kind) : !this._kindClasses(peer).length);\n const role = getAriaRole(section);\n if (role && SECTION_ROLES.has(role)) {\n const name = getElementAccessibleName(section, false);\n if (this._isStableName(name)) {\n const filter = (_a = byIdentity()) != null ? _a : this._textMayName(section, base, [name], ofKind) ? `internal:role=${role}[name=${this._quoteCSSAttributeValue(name)}s]` : null;\n if (!filter)\n return { filter: null };\n if (this._resolvesTo(filter, section))\n return { filter };\n }\n }\n const block = [...section.children].find((c) => c.contains(element));\n const before = (e) => !!block && e !== block && !!(e.compareDocumentPosition(block) & Node.DOCUMENT_POSITION_FOLLOWING);\n let inner = null;\n let text = \"\";\n const heading = [...section.children].find((c) => getAriaRole(c) === \"heading\" && before(c));\n if (heading) {\n text = getElementAccessibleName(heading, false);\n inner = `internal:role=heading[name=${this._quoteCSSAttributeValue(text)}s]`;\n } else {\n const first = section.firstElementChild;\n if (first && before(first) && (/^(HEADER|LEGEND|CAPTION)$/.test(first.tagName) || [...first.classList].some(isHeadClass))) {\n text = this._clean(first.textContent);\n inner = `internal:text=${JSON.stringify(text)}s`;\n }\n }\n if (!inner || !this._isStableName(text))\n return null;\n const twins = [...section.querySelectorAll(SECTION_HEADINGS)].filter((h) => h !== heading && this._clean(h.textContent) === text);\n if (twins.length)\n return null;\n const identity = byIdentity();\n if (identity || !this._textMayName(section, base, [text], ofKind))\n return { filter: identity != null ? identity : null };\n for (const css of sectionCss()) {\n const filter = `${css} >> internal:has=${JSON.stringify(inner)}`;\n if (this._resolvesTo(filter, section))\n return { filter };\n }\n return null;\n }\n /**\n * `element`'s own id or test id as a selector, when it has no digit and no\n * colon (an index or a framework id) and is unique in the document; else\n * null.\n */\n _stableIdFilter(element) {\n for (const attr of [\"data-testid\", \"data-test-id\", \"id\"]) {\n const value = element.getAttribute(attr);\n const filter = value && !/[\\p{Nd}:]/u.test(value) ? `[${attr}=${this._quoteCSSAttributeValue(value)}]` : null;\n if (filter && this._resolvesTo(filter, element))\n return filter;\n }\n return null;\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 const result = scoping.applyScopingHook(element, selector, elements);\n if (result && element.nodeName === \"CANVAS\" && !selectorResolvesTo(injectedScript, result.selector, element))\n return null;\n return result;\n}\nfunction selectorResolvesTo(injectedScript, selector, element) {\n try {\n const matches = injectedScript.querySelectorAll(injectedScript.parseSelector(selector), element.ownerDocument);\n return matches.length === 1 && matches[0] === element;\n } catch {\n return false;\n }\n}\nvar WORD_CHAR = /(?![\\p{Script=Han}\\p{scx=Hiragana}\\p{scx=Katakana}\\p{Script=Thai}\\p{Script=Lao}\\p{Script=Khmer}\\p{Script=Myanmar}])[\\p{L}\\p{M}\\p{N}]/u;\nfunction quotes(label, text) {\n var _a, _b;\n if (!text)\n return false;\n const chars = [...text];\n for (let i = label.indexOf(text); i >= 0; i = label.indexOf(text, i + 1)) {\n const before = (_a = [...label.slice(0, i)].pop()) != null ? _a : \"\";\n const after = (_b = [...label.slice(i + text.length)][0]) != null ? _b : \"\";\n if (!(WORD_CHAR.test(chars[0]) && WORD_CHAR.test(before)) && !(WORD_CHAR.test(chars[chars.length - 1]) && WORD_CHAR.test(after)))\n return true;\n }\n return false;\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, {\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/wheelChunk.ts\nvar NOISE_AXIS_THRESHOLD = 30;\nvar WheelChunk = class {\n constructor(startTime, scrollingElement, setTimeout2) {\n // Of the last event; codegen replays the wheel at the preceding mouse.move.\n this.position = { x: 0, y: 0 };\n this.modifiers = 0;\n this._deltaX = 0;\n this._deltaY = 0;\n this._deltaZ = 0;\n // Offsets of each element the gesture could scroll, taken at the first\n // event in this chunk that reached it.\n this._scrollStart = /* @__PURE__ */ new Map();\n this._prevented = false;\n this.startTime = startTime;\n this._scrollingElement = scrollingElement;\n this._setTimeout = setTimeout2;\n }\n // Call from a capture-phase wheel listener, during dispatch: the snapshot is\n // then usually taken before this event scrolls, and composedPath() is only\n // available while the event is being dispatched.\n add(event, position, modifiers) {\n this._deltaX += event.deltaX;\n this._deltaY += event.deltaY;\n this._deltaZ += event.deltaZ;\n this.position = position;\n this.modifiers = modifiers;\n for (const el of this._scrollChain(event.composedPath())) {\n if (!this._scrollStart.has(el))\n this._scrollStart.set(el, { left: el.scrollLeft, top: el.scrollTop });\n }\n this._setTimeout(() => {\n if (event.defaultPrevented)\n this._prevented = true;\n }, 0);\n }\n get domDelta() {\n return { deltaX: this._deltaX, deltaY: this._deltaY, deltaZ: this._deltaZ };\n }\n isNoise() {\n const scroll = this._measure();\n return Math.max(Math.abs(this._deltaX), Math.abs(scroll.deltaX)) < NOISE_AXIS_THRESHOLD && Math.max(Math.abs(this._deltaY), Math.abs(scroll.deltaY)) < NOISE_AXIS_THRESHOLD;\n }\n recordedDelta() {\n const scroll = this._measure();\n if (!this._prevented && scroll.moved) {\n return {\n deltaX: Math.round(scroll.deltaX * 10) / 10,\n deltaY: Math.round(scroll.deltaY * 10) / 10,\n deltaZ: 0,\n source: \"measured\",\n skippedScrollers: scroll.skipped\n };\n }\n return { ...this.domDelta, source: this._prevented ? \"prevented\" : \"unscrolled\", skippedScrollers: scroll.skipped };\n }\n // The elements on the event's composed path that it can scroll, then the\n // document scroller. The composed path reaches scrollers inside open shadow\n // roots, which event.target (retargeted to the host) does not.\n _scrollChain(path) {\n var _a;\n const chain = [];\n for (const node of path) {\n if (node.nodeType !== 1 || node === this._scrollingElement)\n continue;\n const el = node;\n const style = (_a = el.ownerDocument.defaultView) == null ? void 0 : _a.getComputedStyle(el);\n if (!style)\n continue;\n const scrollableY = el.scrollHeight > el.clientHeight && /^(auto|scroll|overlay)$/.test(style.overflowY);\n const scrollableX = el.scrollWidth > el.clientWidth && /^(auto|scroll|overlay)$/.test(style.overflowX);\n if (scrollableX || scrollableY)\n chain.push(el);\n }\n if (this._scrollingElement)\n chain.push(this._scrollingElement);\n return chain;\n }\n _measure() {\n let deltaX = 0;\n let deltaY = 0;\n let skipped = 0;\n for (const [el, start] of this._scrollStart) {\n if (!el.isConnected || !el.getClientRects().length) {\n skipped++;\n continue;\n }\n deltaX += el.scrollLeft - start.left;\n deltaY += el.scrollTop - start.top;\n }\n return { deltaX, deltaY, moved: Math.abs(deltaX) >= 1 || Math.abs(deltaY) >= 1, skipped };\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 // Wait after scroll block comment to let the page settle\n constructor(recorder) {\n this._dragState = null;\n this._listeners = [];\n this._lastClickTime = 0;\n // A single click waiting out the double-click window, with its position\n // taken at pointerup (before the page's click handlers can scroll).\n // `target` is the clicked element; `record` records the click at once.\n this._pendingClick = null;\n // The positioning context of the last pointerdown and the pointer's offset\n // inside it then (see _contextAt).\n this._clickContext = null;\n this._lastMousePosition = null;\n // Screen position of the last wheel event in the current wheel sequence.\n this._lastWheelScreen = null;\n this._wheelToggleTimeout = null;\n this._isWheelSequence = false;\n // A mouse.wheel was recorded in the current wheel sequence; later chunks of\n // the same gesture (e.g. a fling's momentum tail) are not noise.\n this._wheelSequenceRecorded = false;\n this._wheelChunk = 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 this._cancelPendingClick();\n this._clearWheelTimers();\n this._flushWheelAction();\n this._endWheelSequence();\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 this._cancelPendingClick();\n this._clearWheelTimers();\n this._flushWheelAction();\n this._endWheelSequence();\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 var _a;\n const pendingClick = (_a = this._pendingClick) == null ? void 0 : _a.click;\n this._cancelPendingClick();\n const target = event.target;\n if (!target || this._isPlaywrightElement(target))\n return;\n event.preventDefault();\n const measured = pendingClick != null ? pendingClick : this._positionClick(event);\n const click = measured && this._settledClick(measured);\n if (!click) {\n this._deactivate();\n return;\n }\n try {\n const action = {\n name: \"click\",\n selector: click.selector,\n button: \"left\",\n modifiers: 0,\n clickCount: 2,\n // Double-click\n position: click.position,\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 // A click, double-click or right-click made with the tool replays as\n // locator(selector).click({ position }), with selector body and the\n // body-relative point (#285), except where that point misses after a scroll\n // the recording does not hold (SKYR-4146): see _contextAt. Then the\n // selector is the context's and the position the pointer's offset inside it\n // at pointerdown, before the page could scroll or restyle on the press. The\n // context is dropped if its selector no longer resolves to it when the\n // click is recorded (_settledClick). Null when nothing can be recorded.\n _positionClick(event) {\n const bodyPosition = this._bodyRelativePosition(event);\n const context = this._clickContext;\n this._clickContext = null;\n if (context && context.element.isConnected)\n return { selector: context.selector, position: context.offset, context: context.element, bodyPosition };\n return bodyPosition ? { selector: \"body\", position: bodyPosition, bodyPosition } : null;\n }\n // The click to record: body + the body-relative point if the context's\n // selector no longer resolves to it (the page re-rendered or restyled it).\n _settledClick(click) {\n if (!click.context || selectorResolvesTo(this._recorder.injectedScript, click.selector, click.context))\n return click;\n return click.bodyPosition ? { selector: \"body\", position: click.bodyPosition } : null;\n }\n // The context a click needs to replay after an unrecorded scroll, if any:\n // the nearest position:fixed or sticky element around the point, or, inside\n // a scroll container that has been scrolled, that container's child holding\n // the point (it moves with the scrolled content; replay scrolls it into\n // view). Only with a stable selector (_stableSelector); otherwise, and for\n // everything else, null: body + the body-relative point, as #285.\n _contextAt(x, y) {\n var _a;\n const document2 = this._recorder.document;\n let leaf = document2.elementFromPoint(x, y);\n while (leaf == null ? void 0 : leaf.shadowRoot) {\n const inner = leaf.shadowRoot.elementFromPoint(x, y);\n if (!inner || inner === leaf)\n break;\n leaf = inner;\n }\n if (!leaf || this._isPlaywrightElement(leaf))\n return null;\n const isRoot = (e) => e === document2.body || e === document2.documentElement;\n for (let e = leaf; e && !isRoot(e); e = parentElementOrShadowHost(e)) {\n const position = (_a = getElementComputedStyle(e)) == null ? void 0 : _a.position;\n const parent = parentElementOrShadowHost(e);\n const inScrolled = !!parent && !isRoot(parent) && (parent.scrollTop !== 0 || parent.scrollLeft !== 0) && this._isScrollContainer(parent);\n if (position !== \"fixed\" && position !== \"sticky\" && !inScrolled)\n continue;\n const selector = this._stableSelector(e);\n return selector ? { element: e, selector, offset: this._elementOffset(e, x, y).offset } : null;\n }\n return null;\n }\n // A selector for `element` made only of ids and test ids that resolves to it\n // alone. Roles with accessible names, text and position (nth=) are page data\n // that can differ on replay (e.g. \"Delete 3 files?\", \"Cart (3)\"); null then.\n _stableSelector(element) {\n try {\n const { selector } = this._scopedLikeClick(element, this._recorder.injectedScript.generateSelector(element, {\n testIdAttributeName: this._recorder.state.testIdAttributeName || \"data-testid\"\n }));\n const stablePart = /^\\s*(#[\\w-]+|\\[(id|data-testid)=\"[^\"]*\"\\]|internal:testid=.*)\\s*$/;\n if (!selector.split(\">>\").every((part) => stablePart.test(part)))\n return null;\n return selectorResolvesTo(this._recorder.injectedScript, selector, element) ? selector : null;\n } catch (error) {\n console.warn(\"[PW-RECORDER] No selector for the click context; recording the click on body\", error);\n return null;\n }\n }\n _isScrollContainer(element) {\n const style = getElementComputedStyle(element);\n const scrolls = (overflow) => overflow === \"auto\" || overflow === \"scroll\" || overflow === \"overlay\";\n return scrolls(style == null ? void 0 : style.overflowY) && element.scrollHeight > element.clientHeight || scrolls(style == null ? void 0 : style.overflowX) && element.scrollWidth > element.clientWidth;\n }\n // Where a viewport point falls inside `element`, in the coordinates\n // Playwright's `position` option uses: relative to the padding box\n // (dom.ts _offsetPoint adds the border back, matching offsetX/offsetY).\n // Clamped into the element's border box, so a point measured on its far\n // edge cannot land one pixel outside it (SKYR-4823). `centered` is within\n // 5px of the centre, where replay's default point is as good.\n _elementOffset(element, clientX, clientY) {\n const rect = element.getBoundingClientRect();\n const border = this._recorder.injectedScript.getElementBorderWidth(element);\n const inBox = (v, size) => Math.max(0, Math.min(Math.round(v), Math.floor(size) - 1));\n const x = clientX - rect.left;\n const y = clientY - rect.top;\n return {\n offset: { x: inBox(x, rect.width) - border.left, y: inBox(y, rect.height) - border.top },\n centered: Math.abs(x - rect.width / 2) < 5 && Math.abs(y - rect.height / 2) < 5\n };\n }\n // A drag point, with its offset inside `element` measured now.\n _sample(element, clientX, clientY) {\n return { x: clientX, y: clientY, ...this._elementOffset(element, clientX, clientY) };\n }\n // The body fallback of _positionClick. locator('body').click({ position })\n // resolves the position against body's padding box: a point that moves\n // with the document, not a viewport one (#285).\n // Null without a body: the recorded locator('body') could not replay, and\n // the viewport point is the coordinate this replaces.\n _bodyRelativePosition(event) {\n const body = this._recorder.document.body;\n if (!body)\n return null;\n const rect = body.getBoundingClientRect();\n const border = this._recorder.injectedScript.getElementBorderWidth(body);\n return {\n x: Math.round(event.clientX - rect.left - border.left),\n y: Math.round(event.clientY - rect.top - border.top)\n };\n }\n _endWheelSequence() {\n this._isWheelSequence = false;\n this._wheelSequenceRecorded = false;\n this._lastWheelScreen = null;\n }\n // SKYR-4837: typing into a field while a click still waits out the\n // double-click window. Recorded 300 ms later, the click split the fill into\n // a partial fill, the click and the full fill. A click into that same field\n // only focused it, which the fill does on replay: drop it. Any other click\n // (e.g. a button that moved focus to the field) is recorded now, before the\n // fill. Either way the click ends the tool, as the timeout would.\n beforeArmedTyping(target) {\n const pending = this._pendingClick;\n if (!pending)\n return;\n if (target.contains(pending.target)) {\n this._deactivate();\n return;\n }\n this._cancelPendingClick();\n pending.record();\n }\n _cancelPendingClick() {\n if (this._pendingClick) {\n this._recorder.injectedScript.utils.builtins.clearTimeout(this._pendingClick.timeout);\n this._pendingClick = null;\n }\n }\n // The tool's timers use the injected builtins, which a page's clock mocks\n // (fake timers) do not replace; a timer must be cleared by the same API.\n _clearWheelTimers() {\n const builtins = this._recorder.injectedScript.utils.builtins;\n if (this._wheelToggleTimeout) {\n builtins.clearTimeout(this._wheelToggleTimeout);\n this._wheelToggleTimeout = null;\n }\n if (this._wheelDebounceTimeout) {\n builtins.clearTimeout(this._wheelDebounceTimeout);\n this._wheelDebounceTimeout = null;\n }\n }\n _flushWheelAction() {\n const chunk = this._wheelChunk;\n if (!chunk)\n return;\n this._wheelChunk = null;\n if (!this._wheelSequenceRecorded && chunk.isNoise()) {\n console.log(\"[PW-RECORDER] Suppressing Magic Mouse noise wheel:\", {\n ...chunk.domDelta,\n accumulatedFor: Date.now() - chunk.startTime + \"ms\"\n });\n return;\n }\n const delta = chunk.recordedDelta();\n try {\n const action = {\n name: \"mouse.wheel\",\n position: chunk.position,\n deltaX: delta.deltaX,\n deltaY: delta.deltaY,\n deltaZ: delta.deltaZ,\n modifiers: chunk.modifiers,\n signals: [],\n timestamp: getTimestamp(this._recorder)\n };\n this._recorder.recordAction(action);\n this._wheelSequenceRecorded = true;\n console.log(\"[PW-RECORDER] Flushed accumulated wheel action:\", {\n ...delta,\n domDelta: chunk.domDelta,\n accumulatedFor: Date.now() - chunk.startTime + \"ms\"\n });\n } catch (error) {\n console.error(\"[PW-RECORDER] Error flushing wheel action:\", error);\n }\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 = this._sample(this._dragState.source, dragEvent.clientX, 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 = this._sample(this._dragState.target, dragEvent.clientX, dragEvent.clientY);\n }\n }\n }\n };\n const onPointerDown = (e) => {\n const pointerEvent = e;\n if (this._wheelChunk) {\n if (this._wheelDebounceTimeout) {\n this._recorder.injectedScript.utils.builtins.clearTimeout(this._wheelDebounceTimeout);\n this._wheelDebounceTimeout = null;\n }\n this._flushWheelAction();\n }\n this._clickContext = this._contextAt(pointerEvent.clientX, pointerEvent.clientY);\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 = this._sample(this._dragState.source, pointerEvent.clientX, pointerEvent.clientY);\n } else if (this._isGoJSElement(sourceElement)) {\n this._dragState.isGoJS = true;\n this._dragState.source = sourceElement;\n this._dragState.sourcePoint = this._sample(this._dragState.source, pointerEvent.clientX, pointerEvent.clientY);\n } else if (this._isCanvasElement(sourceElement)) {\n this._dragState.isCanvas = true;\n this._dragState.source = sourceElement;\n this._dragState.sourcePoint = this._sample(this._dragState.source, pointerEvent.clientX, pointerEvent.clientY);\n } else {\n const draggableElement = this._selectDraggableAncestor(sourceElement);\n if (draggableElement !== sourceElement) {\n this._dragState.source = draggableElement;\n this._dragState.sourcePoint = this._sample(this._dragState.source, pointerEvent.clientX, 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 = this._sample(this._dragState.source, pointerEvent.clientX, pointerEvent.clientY);\n } else {\n this._dragState.source = sourceElement;\n this._dragState.sourcePoint = this._sample(this._dragState.source, pointerEvent.clientX, 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 = this._sample(this._dragState.target, pointerEvent.clientX, pointerEvent.clientY);\n } else if (this._dragState.isGoJS && this._isCanvasElement(targetElement)) {\n this._dragState.target = targetElement;\n this._dragState.targetPoint = this._sample(this._dragState.target, pointerEvent.clientX, pointerEvent.clientY);\n } else if (this._dragState.isCanvas && this._isCanvasElement(targetElement)) {\n this._dragState.target = targetElement;\n this._dragState.targetPoint = this._sample(this._dragState.target, pointerEvent.clientX, pointerEvent.clientY);\n } else if (this._dragState.isSlider) {\n this._dragState.target = this._dragState.source;\n this._dragState.targetPoint = this._sample(this._dragState.target, pointerEvent.clientX, 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 = this._sample(this._dragState.target, pointerEvent.clientX, pointerEvent.clientY);\n } else if (targetElement !== this._dragState.source) {\n this._dragState.target = targetElement;\n this._dragState.targetPoint = this._sample(this._dragState.target, pointerEvent.clientX, 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 this._cancelPendingClick();\n const click = this._positionClick(pointerEvent);\n if (!click) {\n this._deactivate();\n return;\n }\n const record = () => {\n this._pendingClick = null;\n const settled = this._settledClick(click);\n if (!settled) {\n this._deactivate();\n return;\n }\n try {\n const action = {\n name: \"click\",\n selector: settled.selector,\n button: \"left\",\n modifiers: 0,\n clickCount: 1,\n position: settled.position,\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 };\n const timeout = this._recorder.injectedScript.utils.builtins.setTimeout(record, 300);\n this._pendingClick = { timeout, click, target: pointerEvent.composedPath()[0], record };\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 = this._sample(this._dragState.target, dragEvent.clientX, 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 = this._sample(this._dragState.target, dragEvent.clientX, 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 const now = Date.now();\n if (this._wheelChunk && now - this._wheelChunk.startTime > _DragDropTool.WHEEL_MAX_ACCUMULATION_MS)\n this._flushWheelAction();\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 const screenPosition = { x: Math.round(wheelEvent.screenX), y: Math.round(wheelEvent.screenY) };\n const pointerMoved = !this._lastWheelScreen || this._lastWheelScreen.x !== screenPosition.x || this._lastWheelScreen.y !== screenPosition.y;\n this._lastWheelScreen = screenPosition;\n if (pointerMoved && (!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 builtins = this._recorder.injectedScript.utils.builtins;\n if (!this._wheelChunk)\n this._wheelChunk = new WheelChunk(now, this._recorder.document.scrollingElement, builtins.setTimeout);\n const chunk = this._wheelChunk;\n if (this._wheelDebounceTimeout)\n builtins.clearTimeout(this._wheelDebounceTimeout);\n this._wheelDebounceTimeout = builtins.setTimeout(() => {\n this._wheelDebounceTimeout = null;\n this._flushWheelAction();\n }, _DragDropTool.WHEEL_DEBOUNCE_MS);\n if (this._wheelToggleTimeout)\n builtins.clearTimeout(this._wheelToggleTimeout);\n this._wheelToggleTimeout = builtins.setTimeout(() => {\n this._deactivate();\n }, _DragDropTool.WHEEL_TOOL_DISABLE_MS);\n chunk.add(wheelEvent, currentPosition, modifiers);\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 const measured = this._positionClick(contextEvent);\n const click = measured && this._settledClick(measured);\n if (!click) {\n this._deactivate();\n return;\n }\n try {\n this._lastMousePosition = { x: Math.round(contextEvent.clientX), y: Math.round(contextEvent.clientY) };\n const action = {\n name: \"click\",\n selector: click.selector,\n button: \"right\",\n modifiers: 0,\n clickCount: 1,\n position: click.position,\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 this._cancelPendingClick();\n this._clearWheelTimers();\n this._endWheelSequence();\n this._wheelChunk = 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 // SKYR-4069: run a generated drag selector through the click recorder's\n // scoping. generateSelector scoped Box's drop target by an id Box numbers by\n // position (`#contextmenutarget427 >> internal:testid=[data-testid=\"item-thumbnail\"]`);\n // Skyramp codegen drops that id as dynamic, and the bare item-thumbnail\n // selector left matches every thumbnail. A click on the same element records\n // the stable `[data-testid=\"grid-view-item\"] >> internal:has-text=\"My Box\n // Notes\"i >> [data-testid=\"item-thumbnail\"]`. The scoped selector is kept only\n // if it still resolves to exactly this element, so scoping never changes\n // which element the drag targets.\n _scopedLikeClick(element, generated) {\n var _a, _b;\n const injected = this._recorder.injectedScript;\n try {\n const elements = (_a = generated.elements) != null ? _a : injected.querySelectorAll(injected.parseSelector(generated.selector), element.ownerDocument);\n const scoped = (_b = applyScopingHook(injected, element, generated.selector, elements)) == null ? void 0 : _b.selector;\n if (scoped && scoped !== generated.selector && selectorResolvesTo(injected, scoped, element))\n return { selector: scoped };\n } catch (e) {\n console.warn(\"[PW-RECORDER] drag selector scoping failed; keeping generated selector\", generated.selector, e);\n }\n return { selector: generated.selector };\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 // 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 _capture() {\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 const generate = (element) => {\n var _a, _b;\n const safe = this._getSelectorSafeElement(element);\n return (_b = (_a = this._stableIdSelector(safe)) != null ? _a : this._stableContainerClassSelector(safe)) != null ? _b : this._scopedLikeClick(safe, this._recorder.injectedScript.generateSelector(safe, {\n testIdAttributeName: this._recorder.state.testIdAttributeName || \"data-testid\"\n }));\n };\n const byTestId = (element, testId) => {\n const selector = `[data-testid=\"${testId}\"]`;\n return selectorResolvesTo(this._recorder.injectedScript, selector, element) ? { selector } : generate(element);\n };\n if (sourceTestId && targetTestId) {\n sourceGenerated = byTestId(this._dragState.source, sourceTestId);\n targetGenerated = byTestId(this._dragState.target, targetTestId);\n } else {\n sourceGenerated = generate(this._dragState.source);\n targetGenerated = generate(this._dragState.target);\n }\n const { sourcePoint, targetPoint } = this._dragState;\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 (sourcePoint && !sourcePoint.centered)\n action.sourcePosition = sourcePoint.offset;\n if (targetPoint && !targetPoint.centered)\n action.targetPosition = targetPoint.offset;\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._scopedLikeClick(canvasElement, this._recorder.injectedScript.generateSelector(canvasElement, {\n testIdAttributeName: this._recorder.state.testIdAttributeName || \"data-testid\"\n }));\n const canvasPosition = (point, sampledOn) => point ? (sampledOn === canvasElement ? point : this._elementOffset(canvasElement, point.x, point.y)).offset : { x: canvasElement.getBoundingClientRect().width / 2, y: canvasElement.getBoundingClientRect().height / 2 };\n const sourcePos = canvasPosition(this._dragState.sourcePoint, canvasElement);\n const targetPos = canvasPosition(this._dragState.targetPoint, this._dragState.target);\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;\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._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 }\n // Escape key exits back to recording mode. Handled here rather than in a\n // document listener so the recorder sees it as consumed by this tool and does\n // not also record it as a key press (SKYR-4117).\n onKeyDown(e) {\n if (e.key === \"Escape\") {\n this._recorder.setMode(\"recording\");\n e.preventDefault();\n e.stopPropagation();\n }\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 }\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, { 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 // SKYR-4117: the assertion dialog is the tool's own UI; typing into it is not recorded.\n ownsEventTarget(target) {\n var _a;\n return !!((_a = this._assertModal) == null ? void 0 : _a.contains(target));\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;\">×</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, \""\")}\"\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 SCOPED_SNAPSHOT_TAGS = /* @__PURE__ */ new Set([\"canvas\", \"img\", \"video\"]);\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 || !this._isExplicitlyScopedTarget(actualTarget) && 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 /**\n * Explicitly scoped targets skip the area threshold: a PDF page wrapper, or a media\n * element (e.g. a full-screen drawing canvas). The user clicked that element, not the page.\n */\n _isExplicitlyScopedTarget(element) {\n return element.matches(\"[data-page-number]\") || SCOPED_SNAPSHOT_TAGS.has(element.tagName.toLowerCase());\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 this._startCanvasRect = canvas.getBoundingClientRect();\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 /**\n * Express the selection against the canvas instead of the viewport.\n *\n * `startPoint`/`endPoint` are clientX/clientY, so on replay they only land on\n * the same part of the canvas if the viewport size, zoom and scroll position\n * all match the recording. Here the two corners are converted to backing-store\n * pixels relative to the canvas, and the canvas/display dimensions are recorded\n * alongside, so a generator can rescale them onto whatever size the canvas has\n * at replay time.\n *\n * Returns undefined for a zero-sized canvas (detached or not yet laid out),\n * which would make the scale factors non-finite.\n */\n _canvasBackingStore(canvas, start, end, startRect) {\n const rect = canvas.getBoundingClientRect();\n if (!rect.width || !rect.height)\n return void 0;\n const scaleX = canvas.width / rect.width;\n const scaleY = canvas.height / rect.height;\n const toBackingStore = (point, box) => ({\n x: Math.round((point.x - box.left) * scaleX),\n y: Math.round((point.y - box.top) * scaleY)\n });\n return {\n start: toBackingStore(start, startRect != null ? startRect : rect),\n end: toBackingStore(end, rect),\n canvasWidth: canvas.width,\n canvasHeight: canvas.height,\n displayWidth: rect.width,\n displayHeight: rect.height\n };\n }\n _isInteractiveElement(element) {\n var _a;\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\"))\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 canvasBackingStore: this._canvasBackingStore(canvas, start, end, this._selectionState.targetCanvas === canvas ? this._startCanvasRect : void 0),\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 this._observer = new MutationObserver((mutations) => this._handleMutations(mutations));\n this._observer.observe(this._document, {\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 this._observer = new MutationObserver((mutations) => this._handleMutations(mutations));\n this._observer.observe(this._document, {\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/skyramp/hoverRevealTracker.ts\nfunction deepElementFromPoint(document2, x, y) {\n var _a;\n let hit = (_a = document2.elementFromPoint(x, y)) != null ? _a : void 0;\n while (hit == null ? void 0 : hit.shadowRoot) {\n const inner = hit.shadowRoot.elementFromPoint(x, y);\n if (!inner || inner === hit)\n break;\n hit = inner;\n }\n return hit;\n}\nvar LABEL_SELECTOR = 'h1, h2, h3, h4, h5, h6, [role=\"heading\"], a[href], [role=\"link\"], label, img[alt], [aria-label], [data-testid], [data-test-id], [data-test], [title]';\nvar OBSERVE_OPTIONS = { childList: true, subtree: true, attributes: true, attributeFilter: [\"href\", \"role\", \"type\"] };\nvar HOVER_SETTLE_MS = 1e3;\nvar INSERT_AFTER_ENTER_MS = 500;\nvar INSERT_WITH_ENTER_MS = 50;\nvar ACTION_SETTLE_MS = 1e3;\nvar LEAVE_GRACE_MS = 700;\nvar HOVER_INTENT_RECHECK_MS = 450;\nvar RECHECK_DELAYS_MS = [0, HOVER_INTENT_RECHECK_MS, LEAVE_GRACE_MS + 200];\nvar SAME_PASS_MS = 5;\nvar PRESS_MATCH_PX = 2;\nvar PRESS_MAX_AGE_MS = 1e3;\nvar MAX_TRIGGER_VIEWPORT_FRACTION = 0.9;\nvar MAX_LABEL_TEXT_LENGTH = 80;\nvar MAX_COMPARED_TEXT_LENGTH = 300;\nvar MAX_OBSERVATIONS = 24;\nvar MAX_CANDIDATES = 400;\nvar MAX_ACTIONS = 200;\nvar MAX_CONTROLS_ON_LEAVE = 60;\nvar MAX_LABELS = 30;\nvar MAX_NESTING = 3;\nvar MAX_RECHECK_ON_TRIGGER = 50;\nvar HoverRevealTracker = class {\n constructor(injectedScript) {\n this._enabled = false;\n this._mutationObserver = null;\n this._intersectionObserver = null;\n this._listeners = [];\n this._sampled = /* @__PURE__ */ new WeakSet();\n // Controls added to the page -> when.\n this._inserted = /* @__PURE__ */ new WeakMap();\n this._watchedShadowRoots = /* @__PURE__ */ new WeakSet();\n this._awaitedDefinitions = /* @__PURE__ */ new Set();\n this._pending = /* @__PURE__ */ new Set();\n this._frameScheduled = false;\n // Controls seen not clickable at least once -> their observations, oldest first.\n this._candidates = /* @__PURE__ */ new Map();\n this._recheck = /* @__PURE__ */ new Set();\n this._recheckAll = false;\n this._recheckScheduled = false;\n // Elements under the pointer (composed ancestry) -> when the pointer moved into each.\n this._hovered = /* @__PURE__ */ new Map();\n this._preHover = /* @__PURE__ */ new WeakMap();\n // Elements the pointer recently left -> when.\n this._left = /* @__PURE__ */ new Map();\n this._actions = [];\n this._testIdAttributeName = \"data-testid\";\n // When the pointer moved onto a popup trigger or into an item (newest last).\n this._pointedEntries = [];\n this._injectedScript = injectedScript;\n this._document = injectedScript.document;\n this._scoping = new ScopingHandler(injectedScript);\n this._clock = injectedScript.isUnderTest ? injectedScript.window : injectedScript.utils.builtins;\n }\n enable() {\n const root = this._document.documentElement;\n if (this._enabled || !root)\n return;\n this._enabled = true;\n const window2 = this._injectedScript.window;\n this._intersectionObserver = new window2.IntersectionObserver((entries) => {\n for (const entry of entries) {\n if (entry.isIntersecting)\n this._queue(entry.target);\n }\n }, { threshold: [0, 0.25, 0.5, 0.75, 1] });\n this._mutationObserver = new window2.MutationObserver((mutations) => {\n for (const mutation of mutations) {\n if (mutation.type === \"attributes\") {\n const element = mutation.target;\n if (isControl(element))\n this._queue(element);\n continue;\n }\n for (const node of mutation.addedNodes) {\n if (node.nodeType === 1)\n this._queueSubtree(node, true);\n }\n }\n });\n this._mutationObserver.observe(root, OBSERVE_OPTIONS);\n const listen = (type, handler) => {\n window2.addEventListener(type, handler, true);\n this._listeners.push(() => window2.removeEventListener(type, handler, true));\n };\n listen(\"mousemove\", (event) => {\n this._confirmFirstEntry(event);\n this._pointer = { x: event.clientX, y: event.clientY };\n });\n listen(\"pointerdown\", (event) => {\n const target = event.composedPath()[0];\n this._pointerDown = (target == null ? void 0 : target.nodeType) === 1 ? { element: target, x: event.clientX, y: event.clientY, time: this._now() } : void 0;\n });\n listen(\"pointerover\", (event) => this._onMouseOver(event));\n listen(\"mouseover\", (event) => this._onMouseOver(event));\n listen(\"mouseout\", (event) => {\n if (!event.relatedTarget)\n this._setHoverChain([], true);\n });\n for (const type of [\"transitionend\", \"transitioncancel\", \"animationend\"])\n listen(type, () => this._scheduleRecheck(\"all\"));\n this._queueSubtree(root, false);\n }\n disable() {\n var _a, _b;\n if (!this._enabled)\n return;\n this._enabled = false;\n (_a = this._mutationObserver) == null ? void 0 : _a.disconnect();\n this._mutationObserver = null;\n (_b = this._intersectionObserver) == null ? void 0 : _b.disconnect();\n this._intersectionObserver = null;\n for (const remove of this._listeners)\n remove();\n this._listeners = [];\n this._watchedShadowRoots = /* @__PURE__ */ new WeakSet();\n this._awaitedDefinitions.clear();\n this._pending.clear();\n this._frameScheduled = false;\n this._sampled = /* @__PURE__ */ new WeakSet();\n this._inserted = /* @__PURE__ */ new WeakMap();\n this._candidates.clear();\n this._recheck.clear();\n this._recheckAll = false;\n this._hovered.clear();\n this._pointerDown = void 0;\n this._pointedEntries = [];\n this._preHover = /* @__PURE__ */ new WeakMap();\n this._left.clear();\n this._pointer = void 0;\n this._firstEntry = void 0;\n this._actions = [];\n }\n // Called from Recorder._decorateUserAction for each non-assert user action,\n // after that action's own hover decision, and again once the recorder has\n // performed it (Recorder.performAction); synthetic hovers are not noted.\n noteUserAction(target) {\n const now = this._now();\n const last = this._actions[this._actions.length - 1];\n if (target && last && last.path[0] === target) {\n last.last = now;\n } else {\n const path = [];\n for (let element = target; element; element = parentElementOrShadowHost(element))\n path.push(element);\n this._actions.push({ first: now, last: now, path });\n if (this._actions.length > MAX_ACTIONS)\n this._actions.shift();\n }\n this._scheduleRecheck(\"all\");\n }\n // The hovers to record, outermost first, before a click on `element`; empty\n // unless a hover is what made its control clickable (see the header).\n hoverStepsFor(element, clickSelector, testIdAttributeName) {\n var _a, _b, _c;\n const control = closestControl(element);\n const timeline = control && this._timeline(control);\n if (!control || !timeline)\n return [];\n const clickIsPositional = (_b = (_a = this._parse(clickSelector)) == null ? void 0 : _a.some((part) => part.name === \"nth\")) != null ? _b : false;\n this._testIdAttributeName = testIdAttributeName;\n return (_c = this._hoverSteps(control, control, timeline, clickIsPositional, 0)) != null ? _c : [];\n }\n // The control's observations, ending with the click itself: it is clickable now.\n _timeline(control) {\n const history = this._candidates.get(control);\n if (!history)\n return;\n const now = this._observe(control);\n return now ? [...history, { ...now, hidden: false, cover: void 0, hiddenAncestors: void 0 }] : history;\n }\n // How `element` differs from when the pointer entered it; undefined when it\n // is not under the pointer or did not change.\n _preHoverChange(element) {\n const before = this._preHover.get(element);\n if (!before)\n return;\n const now = preHoverOf(element);\n const change = { text: before.text !== now.text, ariaLabel: before.ariaLabel !== now.ariaLabel, className: before.className !== now.className, title: before.title !== now.title, before };\n return change.text || change.ariaLabel || change.className || change.title ? change : void 0;\n }\n // A click on an element that changed when hovered (\"Following\" turning into\n // \"Unfollow\") is recorded in its hovered state, but a replay resolves the\n // locator before moving the mouse there. Returns a selector matching the\n // element in both states: a test id, a stable id, its unchanged aria-label,\n // or its name/text as either value. Undefined when the selector does not\n // depend on the change, or when none exists (the hovered selector is kept:\n // the recorder performs the click with it).\n selectorForBothStates(element, selector, testIdAttributeName) {\n const change = this._preHoverChange(element);\n const parts = this._parse(selector);\n if (!change || !parts || !dependsOnChange(parts, change))\n return;\n const prefix = parts.length > 1 ? selector.slice(0, selector.lastIndexOf(\" >> \")) : \"\";\n const candidates = [];\n for (const attribute of [testIdAttributeName, \"data-testid\", \"data-test-id\", \"data-test\"]) {\n const value = element.getAttribute(attribute);\n if (value)\n candidates.push(attribute === testIdAttributeName ? `internal:testid=[${attribute}=${JSON.stringify(value)}s]` : `[${attribute}=${JSON.stringify(value)}]`);\n }\n if (element.id)\n candidates.push(`[id=${JSON.stringify(element.id)}]`);\n const role = getAriaRole(element);\n const ariaLabel = element.getAttribute(\"aria-label\");\n if (role && ariaLabel && !change.ariaLabel)\n candidates.push(`internal:role=${role}[name=${JSON.stringify(ariaLabel)}s]`);\n const last = parts[parts.length - 1];\n const now = textOfPart(last);\n const before = change.ariaLabel ? change.before.ariaLabel : change.before.text;\n let either;\n if (now && before && normalize(before) !== normalize(now)) {\n const pattern = `/^\\\\s*(?:${escapeRegex(before.trim())}|${escapeRegex(now.trim())})\\\\s*$/i`;\n const body = last.name === \"internal:role\" ? last.source.replace(/\\[name=(\"(?:[^\"\\\\]|\\\\.)*\"[is]?)\\]/, `[name=${pattern}]`) : pattern;\n either = `${last.name}=${body}`;\n candidates.push(either);\n }\n for (const candidate of candidates) {\n for (const full of prefix ? [candidate, `${prefix} >> ${candidate}`] : [candidate]) {\n if (!this._scoping._hasDynamicSelector(full) && selectorResolvesTo(this._injectedScript, full, element))\n return full;\n }\n }\n if (either) {\n const full = prefix ? `${prefix} >> ${either}` : either;\n const index = this._matches(full).indexOf(element);\n if (index >= 0 && selectorResolvesTo(this._injectedScript, `${full} >> nth=${index}`, element))\n return `${full} >> nth=${index}`;\n }\n }\n _matches(selector) {\n try {\n return this._injectedScript.querySelectorAll(this._injectedScript.parseSelector(selector), this._document);\n } catch {\n return [];\n }\n }\n // Hovers that reveal `target` (the control, or for nested menus a trigger),\n // judged from the control's timeline; undefined when no hover is needed.\n _hoverSteps(control, target, timeline, clickIsPositional, depth) {\n var _a;\n const reveal = this._trigger(control, target, timeline);\n if (!reveal)\n return;\n const step = this._stableHoverTarget(reveal, control, clickIsPositional);\n if (!step)\n return;\n if (depth + 1 >= MAX_NESTING)\n return [step];\n const outer = (_a = this._hoverSteps(control, step.trigger, timeline, clickIsPositional, depth + 1)) != null ? _a : [];\n return [...outer, step];\n }\n // The hovered elements that made `target` clickable, judged from the\n // control's timeline; undefined when no hover did. Its hovered ancestors\n // when it has any (CSS :hover, a row's mouseenter); otherwise a popup trigger\n // the pointer was on (a menu rendered elsewhere in the page).\n _trigger(control, target, timeline) {\n const hiddenAt = (observation) => {\n var _a;\n return target === control ? observation.hidden : !!((_a = observation.hiddenAncestors) == null ? void 0 : _a.includes(target));\n };\n let firstVisible = timeline.length;\n while (firstVisible > 0 && !hiddenAt(timeline[firstVisible - 1]))\n firstVisible--;\n if (firstVisible === 0 || firstVisible === timeline.length)\n return;\n const hidden = timeline[firstVisible - 1];\n const revealed = timeline[firstVisible];\n const justLeft = [...revealed.left].filter(([element, leftAt]) => !revealed.hovered.has(element) && hidden.hovered.has(element) && revealed.time - leftAt < LEAVE_GRACE_MS);\n const enteredAt = new Map([...revealed.hovered, ...justLeft.map(([element]) => [element, hidden.hovered.get(element)])]);\n const entered = [...enteredAt].filter(([element, time]) => {\n var _a;\n return time >= hidden.time - HOVER_SETTLE_MS && this._canTrigger(element, target) && !((_a = hidden.hiddenAncestors) == null ? void 0 : _a.includes(element));\n });\n const ancestors = entered.filter(([element]) => isInsideScope(element, target));\n const pool = ancestors.length ? ancestors : entered;\n let trigger = pool.filter(([element]) => !hidden.hovered.has(element)).map(([element]) => element);\n if (!trigger.length)\n trigger = pool.map(([element]) => element);\n for (const observation of timeline.slice(firstVisible + 1)) {\n if (hiddenAt(observation))\n continue;\n const hovered = trigger.filter((element) => observation.hovered.has(element));\n const onPopup = [...observation.hovered.keys()].some((element) => isInsideScope(element, target) && !trigger.some((t) => isInsideScope(element, t)));\n if (hovered.length)\n trigger = hovered;\n else if (!onPopup)\n trigger = trigger.filter((element) => {\n var _a;\n return observation.time - ((_a = observation.left.get(element)) != null ? _a : -Infinity) < LEAVE_GRACE_MS;\n });\n }\n if (!trigger.length)\n return;\n const from = Math.min(hidden.time, ...trigger.map((element) => enteredAt.get(element)));\n if (this._actions.some((action) => action.first <= revealed.time && action.last >= from && !action.path.includes(control)))\n return;\n if (target === control && hidden.cover && !trigger.some((element) => isInsideScope(element, hidden.cover)))\n return;\n return { trigger, hiddenAt: hidden.time, enteredAt: Math.min(...trigger.map((element) => enteredAt.get(element))) };\n }\n // Whether the control holding `element` was not clickable when the clicked\n // control was last seen hidden (a sibling hover-only icon of the same row).\n // Siblings are rechecked in the same pass, a few microseconds apart.\n _wasHiddenAt(element, time) {\n var _a;\n const holder = closestControl(element);\n const history = holder && this._candidates.get(holder);\n if (!history)\n return false;\n const before = history.filter((observation) => observation.time <= time + SAME_PASS_MS);\n return !!((_a = before[before.length - 1]) == null ? void 0 : _a.hidden);\n }\n // Whether the control holding `element` was added to the page after the\n // pointer entered the trigger: the hover rendered it, so a replay that has\n // not hovered yet cannot find it.\n _insertedSince(element, time) {\n var _a;\n const holder = closestControl(element);\n return !!holder && ((_a = this._inserted.get(holder)) != null ? _a : -Infinity) >= time;\n }\n // An element a person hovers to reveal `target`: an ancestor of it, or a\n // popup trigger outside it (observations hold no other elements), but not\n // the page itself and not a container as large as the viewport.\n _canTrigger(element, target) {\n if (element === target || isInsideScope(target, element) || element === this._document.body || element === this._document.documentElement)\n return false;\n const box = element.getBoundingClientRect();\n const window2 = this._injectedScript.window;\n return !(box.width >= window2.innerWidth * MAX_TRIGGER_VIEWPORT_FRACTION && box.height >= window2.innerHeight * MAX_TRIGGER_VIEWPORT_FRACTION);\n }\n // Trigger elements from the innermost outwards, up to the first one with a\n // sibling of the same tag (a row among rows, a card among cards; above it a\n // hover could land in another item; a table cell is part of its row, not an\n // item); for each, the element and then the labels inside it (a\n // row's name link). The first whose centre lands inside that trigger\n // element and that has a stable locator wins. A candidate that was itself\n // not clickable before the hover, or that the hover rendered (a sibling\n // hover-only icon), is skipped.\n _stableHoverTarget({ trigger, hiddenAt, enteredAt }, control, clickIsPositional) {\n const levels = [...trigger].sort((a, b) => isInsideScope(b, a) ? -1 : 1);\n const item = levels.findIndex(isItem);\n if (item >= 0)\n levels.splice(item + 1);\n const tried = /* @__PURE__ */ new Set();\n for (const level of levels) {\n const labels = [...level.querySelectorAll(LABEL_SELECTOR), ...this._textLeaves(level)].filter((element) => !isInsideScope(element, control) && !isInsideScope(control, element)).slice(0, MAX_LABELS);\n for (const candidate of [level, ...labels]) {\n if (tried.has(candidate))\n continue;\n tried.add(candidate);\n if (!isElementVisible(candidate) || this._wasHiddenAt(candidate, hiddenAt) || this._insertedSince(candidate, enteredAt))\n continue;\n const point = center(candidate.getBoundingClientRect());\n const hit = deepElementFromPoint(this._document, point.x, point.y);\n if (!hit || !isInsideScope(level, hit) || !isInsideScope(candidate, hit))\n continue;\n const selector = this._stableSelector(candidate, control, clickIsPositional);\n if (selector)\n return { selector, trigger: level };\n }\n }\n }\n // Elements holding their own short text (a widget title rendered as a div).\n _textLeaves(root) {\n var _a, _b;\n const leaves = [];\n const walker = this._document.createTreeWalker(\n root,\n 4\n /* NodeFilter.SHOW_TEXT */\n );\n for (let node = walker.nextNode(); node && leaves.length < MAX_LABELS; node = walker.nextNode()) {\n const text = (_b = (_a = node.nodeValue) == null ? void 0 : _a.trim()) != null ? _b : \"\";\n const parent = node.parentElement;\n if (text && text.length <= MAX_LABEL_TEXT_LENGTH && parent && parent !== root && !leaves.includes(parent))\n leaves.push(parent);\n }\n return leaves;\n }\n _stableSelector(element, control, clickIsPositional) {\n let selector;\n try {\n selector = this._injectedScript.generateSelector(element, { testIdAttributeName: this._testIdAttributeName }).selector;\n } catch {\n return;\n }\n if (!selector || !this._isStableSelector(selector, control, clickIsPositional))\n return;\n const change = this._preHoverChange(element);\n return change && dependsOnChange(this._parse(selector), change) ? void 0 : selector;\n }\n // No position (unless the click is positional too, e.g. the second of two\n // identical widgets), no generated id or CSS-in-JS class, no bare tag path,\n // and no text concatenated from several elements or taken from the control.\n _isStableSelector(selector, control, clickIsPositional) {\n const parts = this._parse(selector);\n if (!parts || this._scoping._hasDynamicSelector(selector))\n return false;\n for (let i = 0; i < parts.length; i++) {\n const part = parts[i];\n if (part.name === \"nth\") {\n if (!clickIsPositional)\n return false;\n continue;\n }\n if (part.name === \"css\") {\n const css = part.source;\n if (/:(nth-|first-|last-|only-)/.test(css) || !/[#.[]/.test(css))\n return false;\n continue;\n }\n const text = textOfPart(part);\n if (text && !this._isOwnText(parts.slice(0, i + 1), part.name, text, control))\n return false;\n }\n return true;\n }\n // Text a hover locator may match on: inside one text node of the matched\n // element (\"CancelReset changesSave\" spans several), and not depending on the\n // control the hover reveals. A role name or text of an element that holds\n // the control includes the control's name only once it is revealed (\"Beta\n // plan Draft Delete\"), so only a has-text filter on a text node outside the\n // control may be used there.\n _isOwnText(prefix, name, text, control) {\n var _a, _b, _c;\n let element;\n try {\n element = this._injectedScript.querySelectorAll({ parts: prefix }, this._document)[0];\n } catch {\n return false;\n }\n if (!element)\n return false;\n const holdsControl = isInsideScope(element, control);\n if (holdsControl && name !== \"internal:has-text\")\n return false;\n const needle = normalize(text);\n const walker = this._document.createTreeWalker(\n element,\n 4\n /* NodeFilter.SHOW_TEXT */\n );\n for (let node = walker.nextNode(); node; node = walker.nextNode()) {\n if (normalize((_a = node.nodeValue) != null ? _a : \"\").includes(needle) && !isInsideScope(control, (_b = node.parentElement) != null ? _b : void 0))\n return true;\n }\n return !holdsControl && !normalize((_c = element.textContent) != null ? _c : \"\").includes(needle);\n }\n _parse(selector) {\n try {\n return this._injectedScript.parseSelector(selector).parts;\n } catch {\n return void 0;\n }\n }\n _onMouseOver(event) {\n const first = !this._pointer;\n const moved = !first && !samePixel(this._pointer, event);\n this._pointer = { x: event.clientX, y: event.clientY };\n const target = event.composedPath()[0];\n const chain = [];\n for (let element = (target == null ? void 0 : target.nodeType) === 1 ? target : void 0; element; element = parentElementOrShadowHost(element))\n chain.push(element);\n if (first)\n this._firstEntry = { x: event.clientX, y: event.clientY, time: this._now(), chain };\n else if (this._firstEntry && (this._firstEntry.x !== event.clientX || this._firstEntry.y !== event.clientY))\n this._firstEntry = void 0;\n this._setHoverChain(chain, moved);\n }\n // A real entry from outside the page is followed by a mousemove at the same\n // point; a page rendering under a resting pointer is not. Credit the entry\n // then, at the time it happened.\n _confirmFirstEntry(event) {\n const entry = this._firstEntry;\n this._firstEntry = void 0;\n if (!entry || entry.x !== event.clientX || entry.y !== event.clientY)\n return;\n const changed = entry.chain.filter((element) => this._hovered.get(element) === -Infinity);\n if (!changed.length)\n return;\n for (const element of changed)\n this._hovered.set(element, entry.time);\n for (const control of this._candidates.keys()) {\n if (changed.some((element) => element !== control && isInsideScope(element, control)))\n this._recheck.add(control);\n }\n this._scheduleRecheck();\n }\n // Updates what is under the pointer and rechecks the controls whose\n // ancestors the pointer entered or left. A chain change without pointer\n // movement (layout, scrolling, an overlay opening under the cursor) is not a\n // hover by the person.\n _setHoverChain(chain, moved) {\n var _a;\n const now = this._now();\n const next = new Set(chain);\n const changed = [];\n if (moved && chain.some((element) => !this._hovered.has(element) && (isTrigger(element) || isItem(element)))) {\n this._pointedEntries.push(now);\n if (this._pointedEntries.length > MAX_ACTIONS)\n this._pointedEntries.shift();\n }\n for (const element of this._hovered.keys()) {\n if (!next.has(element)) {\n this._hovered.delete(element);\n this._preHover.delete(element);\n this._left.set(element, now);\n changed.push(element);\n this._sampleControlsInside(element);\n }\n }\n for (const element of chain) {\n if (!this._hovered.has(element)) {\n this._hovered.set(element, moved ? now : -Infinity);\n this._preHover.set(element, preHoverOf(element));\n changed.push(element);\n }\n }\n if (!changed.length)\n return;\n for (const [element, time] of this._left) {\n if (now - time > LEAVE_GRACE_MS || next.has(element))\n this._left.delete(element);\n }\n for (const control of this._candidates.keys()) {\n if (changed.some((element) => element !== control && isInsideScope(element, control)))\n this._recheck.add(control);\n }\n if (changed.some((element) => isTrigger(element))) {\n let budget = changed.some(isPopupTrigger) ? Infinity : MAX_RECHECK_ON_TRIGGER;\n for (const [control, history] of [...this._candidates].reverse()) {\n if (budget <= 0)\n break;\n if (((_a = history[history.length - 1]) == null ? void 0 : _a.hidden) && !this._recheck.has(control)) {\n this._recheck.add(control);\n budget--;\n }\n }\n }\n this._scheduleRecheck();\n }\n // A control that was clickable when first sampled (the pointer rested on its\n // card) is checked again once the pointer leaves its card.\n _sampleControlsInside(element) {\n if (element === this._document.body || element === this._document.documentElement)\n return;\n const controls = controlsIn(element);\n if (controls.length > MAX_CONTROLS_ON_LEAVE)\n return;\n for (const control of controls) {\n if (!this._candidates.has(control))\n this._recheck.add(control);\n }\n }\n _scheduleRecheck(all) {\n if (all)\n this._recheckAll = true;\n if (this._recheckScheduled || !this._enabled)\n return;\n this._recheckScheduled = true;\n this._clock.requestAnimationFrame(() => {\n this._recheckScheduled = false;\n const controls = this._recheckAll ? [...this._candidates.keys(), ...this._recheck] : [...this._recheck];\n this._recheckAll = false;\n this._recheck.clear();\n this._recheckNow(controls);\n for (const delay of RECHECK_DELAYS_MS.slice(1))\n this._clock.setTimeout(() => this._recheckNow(controls), delay);\n });\n }\n _recheckNow(controls) {\n if (!this._enabled)\n return;\n for (const control of controls) {\n try {\n this._record(control);\n } catch (e) {\n this._log(\"recheck failed\", control, e);\n }\n }\n }\n // Adds an observation to a candidate; a control seen hidden becomes one.\n _record(control, observation = this._observe(control)) {\n if (!observation)\n return;\n let history = this._candidates.get(control);\n if (!history) {\n if (!observation.hidden)\n return;\n history = [];\n this._addCandidate(control, history);\n }\n const last = history[history.length - 1];\n if (last && last.hidden === observation.hidden && sameKeys(last.hovered, observation.hovered) && sameElements(last.hiddenAncestors, observation.hiddenAncestors))\n history.pop();\n history.push(observation);\n if (history.length > MAX_OBSERVATIONS)\n history.shift();\n }\n _addCandidate(control, history) {\n if (this._candidates.size >= MAX_CANDIDATES) {\n for (const element of this._candidates.keys()) {\n if (!element.isConnected)\n this._candidates.delete(element);\n }\n if (this._candidates.size >= MAX_CANDIDATES)\n this._candidates.delete(this._candidates.keys().next().value);\n }\n this._candidates.set(control, history);\n }\n // Can the control be clicked now, the way Playwright decides it? Undefined\n // when that cannot be told (detached, or its centre outside the viewport).\n _observe(control) {\n if (!control.isConnected)\n return;\n const relevant = (element) => element !== control && (isInsideScope(element, control) || !isInsideScope(control, element) && isTrigger(element));\n const hovered = new Map([...this._hovered].filter(([element]) => relevant(element)));\n const left = new Map([...this._left].filter(([element]) => relevant(element)));\n const observation = { time: this._now(), hidden: false, hovered, left };\n if (!isElementVisible(control)) {\n observation.hidden = true;\n } else {\n const box = control.getBoundingClientRect();\n if (!this._inViewport(box))\n return;\n const point = center(box);\n if (this._injectedScript.expectHitTarget(point, control) !== \"done\") {\n observation.hidden = true;\n observation.cover = deepElementFromPoint(this._document, point.x, point.y);\n }\n }\n if (observation.hidden) {\n observation.hiddenAncestors = [];\n for (let ancestor = parentElementOrShadowHost(control); ancestor && ancestor !== this._document.body; ancestor = parentElementOrShadowHost(ancestor)) {\n if (!isElementVisible(ancestor))\n observation.hiddenAncestors.push(ancestor);\n }\n }\n return observation;\n }\n _queueSubtree(root, inserted) {\n if (isControl(root))\n this._queue(root, inserted);\n if (root.matches(\":not(:defined)\"))\n this._awaitDefinition(root.localName);\n if (root.shadowRoot)\n this._watchShadowRoot(root.shadowRoot, inserted);\n this._queueScope(root, inserted);\n }\n _queueScope(scope, inserted) {\n for (const control of controlsIn(scope))\n this._queue(control, inserted);\n for (const element of scope.querySelectorAll(\"*\")) {\n if (element.shadowRoot)\n this._watchShadowRoot(element.shadowRoot, inserted);\n }\n for (const element of scope.querySelectorAll(\":not(:defined)\"))\n this._awaitDefinition(element.localName);\n }\n // Upgrading a custom element that is already in the page attaches its shadow\n // root without any childList mutation, so rescan once its definition lands.\n _awaitDefinition(name) {\n const registry = this._injectedScript.window.customElements;\n if (!registry || this._awaitedDefinitions.has(name))\n return;\n this._awaitedDefinitions.add(name);\n registry.whenDefined(name).then(() => {\n const root = this._document.documentElement;\n if (this._enabled && root)\n this._queueSubtree(root, false);\n }).catch(() => {\n });\n }\n _watchShadowRoot(shadowRoot, inserted) {\n var _a;\n if (this._watchedShadowRoots.has(shadowRoot))\n return;\n this._watchedShadowRoots.add(shadowRoot);\n (_a = this._mutationObserver) == null ? void 0 : _a.observe(shadowRoot, OBSERVE_OPTIONS);\n this._queueScope(shadowRoot, inserted);\n }\n _queue(element, inserted = false) {\n if (this._sampled.has(element))\n return;\n if (inserted && !this._inserted.has(element))\n this._inserted.set(element, this._now());\n this._pending.add(element);\n if (this._frameScheduled)\n return;\n this._frameScheduled = true;\n this._clock.requestAnimationFrame(() => this._flush());\n }\n _flush() {\n this._frameScheduled = false;\n if (!this._enabled)\n return;\n const pending = [...this._pending];\n this._pending.clear();\n for (const element of pending) {\n if (!element.isConnected || this._sampled.has(element))\n continue;\n try {\n this._sample(element);\n } catch (e) {\n this._log(\"sampling failed\", element, e);\n }\n }\n }\n _log(...args) {\n if (this._injectedScript.window.__SKYRAMP_DEBUG__)\n console.log(\"[HoverRevealTracker]\", ...args);\n }\n _sample(control) {\n var _a, _b;\n const observation = this._observe(control);\n if (!observation) {\n if (control.isConnected)\n (_a = this._intersectionObserver) == null ? void 0 : _a.observe(control);\n return;\n }\n this._sampled.add(control);\n (_b = this._intersectionObserver) == null ? void 0 : _b.unobserve(control);\n const insertedAt = this._inserted.get(control);\n if (observation.hidden || insertedAt === void 0) {\n this._record(control, observation);\n return;\n }\n const lastAction = this._lastActionTime();\n const enteredBy = ([element, time]) => this._renderedByEntry(time, insertedAt, lastAction, !isInsideScope(element, control));\n if (![...observation.hovered].some(enteredBy))\n return;\n const before = new Map([...observation.hovered].filter((entry) => !enteredBy(entry)));\n const history = [{ time: observation.time, hidden: true, hovered: before, hiddenAncestors: [], left: /* @__PURE__ */ new Map() }, observation];\n this._addCandidate(control, history);\n }\n // Whether the pointer moving into an element at `enteredAt` rendered a\n // control added at `insertedAt`. The entry comes after the last user action\n // and before the insert, and the insert follows it closely: within\n // INSERT_WITH_ENTER_MS (nothing but the entry is that fast), or within\n // INSERT_AFTER_ENTER_MS (HOVER_SETTLE_MS for a trigger outside the control,\n // `elsewhere`: a menu rendered elsewhere after a hover-intent delay, where\n // nothing loads under the pointer) when no user action explains it better (none in the\n // ACTION_SETTLE_MS before the insert, or the pointer moved onto a popup\n // trigger or into an item with or after the entry; a mouseenter handler that\n // renders synchronously holds back the next pointer events, so up to\n // INSERT_WITH_ENTER_MS after the insert).\n _renderedByEntry(enteredAt, insertedAt, lastAction, elsewhere) {\n if (enteredAt > insertedAt || enteredAt <= lastAction)\n return false;\n if (enteredAt >= insertedAt - INSERT_WITH_ENTER_MS)\n return true;\n const unexplained = insertedAt - lastAction >= ACTION_SETTLE_MS || this._pointedEntries.some((time) => time >= enteredAt && time <= insertedAt + INSERT_WITH_ENTER_MS);\n return unexplained && enteredAt >= insertedAt - (elsewhere ? HOVER_SETTLE_MS : INSERT_AFTER_ENTER_MS);\n }\n _lastActionTime() {\n var _a, _b;\n return (_b = (_a = this._actions[this._actions.length - 1]) == null ? void 0 : _a.last) != null ? _b : -Infinity;\n }\n // The element pressed at a viewport point in the last second: a click\n // recorded after a delay (the Drag-and-drop tool's double-click wait) is\n // resolved against the page as it was when pressed.\n pressedAt(x, y) {\n const down = this._pointerDown;\n if (down && Math.abs(down.x - x) <= PRESS_MATCH_PX && Math.abs(down.y - y) <= PRESS_MATCH_PX && this._now() - down.time < PRESS_MAX_AGE_MS)\n return down.element;\n }\n _inViewport(box) {\n const { x, y } = center(box);\n const window2 = this._injectedScript.window;\n return x >= 0 && y >= 0 && x < window2.innerWidth && y < window2.innerHeight;\n }\n _now() {\n return this._clock.performance.now();\n }\n};\nfunction samePixel(a, event) {\n return Math.floor(a.x) === Math.floor(event.clientX) && Math.floor(a.y) === Math.floor(event.clientY);\n}\nfunction isItem(element) {\n var _a;\n if (isCell(element))\n return false;\n for (let sibling = (_a = element.parentElement) == null ? void 0 : _a.firstElementChild; sibling; sibling = sibling.nextElementSibling) {\n if (sibling !== element && sibling.localName === element.localName)\n return true;\n }\n return false;\n}\nfunction center(box) {\n return { x: box.left + box.width / 2, y: box.top + box.height / 2 };\n}\nfunction sameKeys(a, b) {\n if (a.size !== b.size)\n return false;\n for (const [key, value] of a) {\n if (b.get(key) !== value)\n return false;\n }\n return true;\n}\nfunction preHoverOf(element) {\n var _a, _b;\n const text = (_a = element.textContent) != null ? _a : \"\";\n return { text: text.length <= MAX_COMPARED_TEXT_LENGTH ? text.replace(/\\s+/g, \" \").trim() : void 0, ariaLabel: element.getAttribute(\"aria-label\"), className: (_b = element.getAttribute(\"class\")) != null ? _b : \"\", title: element.getAttribute(\"title\") };\n}\nfunction dependsOnChange(parts, change) {\n const nameChanged = change.text || change.ariaLabel || change.title;\n return parts.some((part) => {\n if (part.name === \"css\")\n return change.className && part.source.includes(\".\") || change.ariaLabel && part.source.includes(\"aria-label\") || change.title && part.source.includes(\"title\");\n if (part.name === \"internal:role\")\n return nameChanged && part.source.includes(\"[name=\");\n if (part.name === \"internal:text\" || part.name === \"internal:has-text\" || part.name === \"text\" || part.name === \"internal:label\")\n return nameChanged;\n if (part.name === \"internal:attr\")\n return change.ariaLabel && part.source.includes(\"aria-label\") || change.title && part.source.includes(\"title\");\n return false;\n });\n}\nfunction escapeRegex(text) {\n return text.replace(/[.*+?^${}()|[\\]\\\\/]/g, \"\\\\$&\");\n}\nfunction sameElements(a, b) {\n var _a, _b;\n return ((_a = a == null ? void 0 : a.length) != null ? _a : 0) === ((_b = b == null ? void 0 : b.length) != null ? _b : 0) && (a != null ? a : []).every((element, i) => b[i] === element);\n}\nfunction normalize(text) {\n return text.replace(/\\s+/g, \" \").trim().toLowerCase();\n}\nfunction textOfPart(part) {\n var _a, _b;\n if (part.name === \"internal:text\" || part.name === \"internal:has-text\" || part.name === \"text\")\n return unquote(part.source);\n if (part.name === \"internal:role\")\n return unquote((_b = (_a = /\\[name=(\"(?:[^\"\\\\]|\\\\.)*\"[is]?)\\]/.exec(part.source)) == null ? void 0 : _a[1]) != null ? _b : \"\");\n}\nfunction unquote(body) {\n const match = /^\"((?:[^\"\\\\]|\\\\.)*)\"[is]?$/.exec(body.trim());\n return match ? JSON.parse(`\"${match[1]}\"`) : void 0;\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 kRecordsUserInputWhileArmed = {\n \"recordingDrag\": true,\n \"recordingGoJSLink\": true,\n \"recordingArea\": true,\n \"recordingSketchTool\": true,\n \"recordingTableSnapshot\": true,\n \"assertingVSnapshot\": true,\n \"assertingTableCell\": true,\n // Already forwards input and keys to the RecordActionTool itself.\n \"recordingFileUpload\": false,\n // One-shot: captures on install and returns to recording right away.\n \"recordingDomSnapshot\": false,\n // The RecordActionTool itself, and the upstream assertion and inspection tools.\n \"recording\": false,\n \"assertingText\": false,\n \"assertingVisibility\": false,\n \"assertingValue\": false,\n \"assertingSnapshot\": false,\n \"inspecting\": false,\n \"recording-inspecting\": false,\n \"none\": false,\n \"standby\": false,\n \"replaying\": false\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 this._cancelFillReread();\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 // Read before the click is performed: the page has not seen the\n // pointer events yet (they are consumed above), so this is the state\n // the user's click acted on. undefined (no expanded state) is dropped\n // by JSON.stringify, so such a click serialises as before.\n expanded: this._recorder.expandedStateOf(targetSelector),\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 // SKYR-4117: record a key the page receives natively while an armed tool is\n // active: the same press, check or uncheck as onKeyDown, but record only.\n recordNativeKeyDown(event) {\n var _a, _b;\n if (this._dialog.isShowing() || !this._shouldGenerateKeyPressFor(event))\n return;\n const target = this._recorder.deepEventTarget(event);\n if (((_a = this._activeModel) == null ? void 0 : _a.elements[0]) !== target)\n this._onFocus(false);\n if (((_b = this._activeModel) == null ? void 0 : _b.elements[0]) !== target)\n return;\n const checkbox = event.key === \" \" ? asCheckbox(target) : null;\n if (checkbox && event.detail === 0) {\n this._recordAction({ name: checkbox.checked ? \"uncheck\" : \"check\", selector: this._activeModel.selector, signals: [], timestamp: getTimestamp10(this._recorder) });\n return;\n }\n this._recordAction({\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 isPerformingAction() {\n return this._performingActions.size > 0;\n }\n // SKYR-4117: record a click the app already received natively while an armed\n // tool let it through. Same selector as a hover in normal mode; record only.\n recordNativeClick(event) {\n const target = this._recorder.deepEventTarget(event);\n if (this._shouldIgnoreMouseEvent(event) || isRangeInput(target) || this._findFileInput(target))\n return;\n let { selector, elements } = this._recorder.injectedScript.generateSelector(target, { testIdAttributeName: this._recorder.state.testIdAttributeName });\n const scopingResult = applyScopingHook(this._recorder.injectedScript, target, selector, elements);\n if (scopingResult)\n selector = scopingResult.selector;\n const checkbox = asCheckbox(target);\n if (checkbox) {\n this._recordAction({ name: checkbox.checked ? \"check\" : \"uncheck\", selector, signals: [], timestamp: getTimestamp10(this._recorder) });\n return;\n }\n this._recordAction({\n name: \"click\",\n 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 }\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 // The pending single click was cancelled above, so the page has not\n // seen this click yet either.\n expanded: this._recorder.expandedStateOf(this._hoveredModel.selector),\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 this._pointer = { x: event.clientX, y: event.clientY };\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 const fill = {\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 this._recordAction(fill);\n const widgets = target.isContentEditable ? [...target.querySelectorAll('[contenteditable=\"false\"]')] : [];\n if (widgets.length) {\n this._fillReread = this._recorder.injectedScript.utils.builtins.setTimeout(() => {\n this._fillReread = void 0;\n if (target.isConnected && widgets.some((w) => !target.contains(w)) && target.innerText !== fill.text)\n this._recordAction({ ...fill, text: target.innerText });\n }, 0);\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 expanded: this._recorder.expandedStateOf(model.selector),\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 expanded: this._recorder.expandedStateOf(model.selector),\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 expanded: this._recorder.expandedStateOf(model.selector),\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._cancelFillReread();\n this._recorder.recordAction(action);\n }\n _cancelFillReread() {\n if (this._fillReread !== void 0)\n this._recorder.injectedScript.utils.builtins.clearTimeout(this._fillReread);\n this._fillReread = void 0;\n }\n _performAction(action) {\n this._cancelFillReread();\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 this._refreshHoverUnderPointer();\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 // The hovered model is frozen while an action is performed. If the action put\n // something new under the still pointer (a Radix select opening its options\n // over the trigger), no move reaches it, and the next click would record the\n // stale model (the trigger, not the option). Take what is under the pointer\n // now, not what the page focused meanwhile.\n _refreshHoverUnderPointer() {\n var _a, _b;\n if (!this._pointer || this._performingActions.size || this._dialog.isShowing())\n return;\n if (!((_a = this._hoveredElement) == null ? void 0 : _a.isConnected)) {\n this._updateModelForHoveredElement();\n return;\n }\n const hit = deepElementFromPoint(this._recorder.document, this._pointer.x, this._pointer.y);\n if (!hit || hit.nodeName === \"X-PW-GLASS\" || ((_b = this._recorder.overlay) == null ? void 0 : _b.contains(hit)))\n return;\n this._hoveredElement = hit;\n this._updateModelForHoveredElement();\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 {\n constructor(injectedScript, options) {\n this._listeners = [];\n // SKYR-4117: see _recordClickLetThrough.\n this._recordedActionCount = 0;\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 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 if ((options == null ? void 0 : options.recorderMode) !== \"api\")\n this._hoverRevealTracker = new HoverRevealTracker(injectedScript);\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;\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 const activeEl = deepActiveElement(this.document);\n let typingTool;\n if (this.state.mode === \"recording\")\n typingTool = this._currentTool;\n else if (this._armedUserInputTarget(activeEl))\n typingTool = this._armedRecordTool();\n if (typingTool && activeEl && activeEl !== this.document.body && activeEl !== this.document.documentElement)\n (_e = typingTool.onFocus) == null ? void 0 : _e.call(typingTool, new FocusEvent(\"focus\"));\n const cursor = (_f = newTool.cursor) == null ? void 0 : _f.call(newTool);\n if (cursor)\n (_g = this.injectedScript.document.body) == null ? void 0 : _g.setAttribute(\"data-pw-cursor\", cursor);\n }\n setUIState(state, delegate) {\n var _a, _b, _c, _d;\n this._delegate = delegate;\n observeClassChanges(this.document, state.mode !== \"none\" && state.mode !== \"standby\" && state.mode !== \"inspecting\" && state.mode !== \"replaying\");\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 if (state.mode === \"recording\" || state.mode === \"recordingDrag\")\n (_c = this._hoverRevealTracker) == null ? void 0 : _c.enable();\n else\n (_d = this._hoverRevealTracker) == null ? void 0 : _d.disable();\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 const preventedBefore = event.defaultPrevented;\n (_c = (_b = this._currentTool).onClick) == null ? void 0 : _c.call(_b, event);\n this._recordClickLetThrough(event, preventedBefore);\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 this._armedPointerDown = this._armedRecordTool() && event.button === 0 ? { target: this.deepEventTarget(event), actionCount: this._recordedActionCount } : void 0;\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 (_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 _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, _c, _d;\n if (this._ignoreOverlayEvent(event))\n return;\n (_b = (_a = this._currentTool).onFocus) == null ? void 0 : _b.call(_a, event);\n if (this._armedUserInputTarget(this.deepEventTarget(event)))\n (_d = (_c = this._armedRecordTool()) == null ? void 0 : _c.onFocus) == null ? void 0 : _d.call(_c, 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, _c, _d, _e, _f;\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 const target = this.deepEventTarget(event);\n if (isTypingTarget(target) && this._armedUserInputTarget(target)) {\n (_d = (_c = this._currentTool).beforeArmedTyping) == null ? void 0 : _d.call(_c, target);\n (_f = (_e = this._armedRecordTool()) == null ? void 0 : _e.onInput) == null ? void 0 : _f.call(_e, event);\n }\n }\n _onKeyDown(event) {\n var _a, _b;\n if (!event.isTrusted)\n return;\n if (this._ignoreOverlayEvent(event))\n return;\n const preventedBefore = event.defaultPrevented;\n (_b = (_a = this._currentTool).onKeyDown) == null ? void 0 : _b.call(_a, event);\n this._forwardKey(event, preventedBefore);\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 // SKYR-4117: the RecordActionTool, when the armed tool leaves the user's\n // typing and plain clicks to it.\n _armedRecordTool() {\n const tool = this._tools.recording;\n return kRecordsUserInputWhileArmed[this.state.mode] && tool instanceof RecordActionTool ? tool : void 0;\n }\n // SKYR-4117: a page element (not the tool's surface or its own UI) whose\n // focus, typing and keys are recorded as in normal mode while a tool is armed.\n _armedUserInputTarget(target) {\n var _a, _b;\n return !!this._armedRecordTool() && !isToolSurface(target) && !((_b = (_a = this._currentTool).ownsEventTarget) == null ? void 0 : _b.call(_a, target));\n }\n // SKYR-4117: the armed tool sees each key first and keeps the keys it\n // consumes (Escape to cancel). Any other key on a page element is recorded\n // as in normal mode, including keys the page itself prevents, but record\n // only: the key reaches the page natively, so nothing is re-performed.\n _forwardKey(event, preventedBefore) {\n if (event.defaultPrevented && !preventedBefore)\n return;\n const tool = this._armedRecordTool();\n if (tool && this._armedUserInputTarget(this.deepEventTarget(event)))\n tool.recordNativeKeyDown(event);\n }\n // SKYR-4117: while a tool is armed, a plain click on a page element that the\n // tool let through (it did not prevent the click and recorded no gesture of\n // its own since pointerdown, and pointerdown and click hit the same element)\n // already reached the app natively. Record it, without consuming and\n // re-performing it. A drag across elements fires its click on their common\n // ancestor, so a target change means a gesture, not a click.\n _recordClickLetThrough(event, preventedBefore) {\n const down = this._armedPointerDown;\n this._armedPointerDown = void 0;\n const tool = this._armedRecordTool();\n if (!tool || !down || event.button !== 0 || event.detail > 1)\n return;\n if (this.state.mode === \"recordingDrag\")\n return;\n if (event.defaultPrevented && !preventedBefore)\n return;\n if (this._recordedActionCount !== down.actionCount || tool.isPerformingAction())\n return;\n const target = this.deepEventTarget(event);\n if (target !== down.target || !this._armedUserInputTarget(target))\n return;\n tool.recordNativeClick(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, _c;\n this._decorateUserAction(action);\n const target = this._hoverRevealTracker && \"selector\" in action && action.selector ? this._resolveActionTarget(action.selector) : void 0;\n await ((_b = (_a = this._delegate).performAction) == null ? void 0 : _b.call(_a, action).catch(() => {\n }));\n (_c = this._hoverRevealTracker) == null ? void 0 : _c.noteUserAction(target);\n }\n // Updates the aria-snapshot baseline, computes preconditionSelector for\n // non-assert actions, and (for clicks) synthesizes the hovers that made the\n // click possible, as the SKYR-4096 tracker judges them. Then notes the action\n // for the tracker. 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, _b, _c;\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 const clickTarget = action.name === \"click\" ? this._clickTarget(action) : void 0;\n const previousUserHover = ((_b = this._lastUserHoverTarget) == null ? void 0 : _b.isConnected) ? this._lastUserHoverTarget : void 0;\n this._lastUserHoverTarget = action.name === \"hover\" ? this._resolveActionTarget(action.selector) : void 0;\n if ((clickTarget == null ? void 0 : clickTarget.isConnected) && this._hoverRevealTracker) {\n const bothStates = this._hoverRevealTracker.selectorForBothStates(clickTarget, action.selector, this.state.testIdAttributeName);\n if (bothStates)\n action.selector = bothStates;\n this._maybeEmitHoverRevealBeforeClick(action, clickTarget, previousUserHover);\n }\n (_c = this._hoverRevealTracker) == null ? void 0 : _c.noteUserAction(\"selector\" in action && action.selector ? this._resolveActionTarget(action.selector) : void 0);\n }\n // SKYR-4096: if a hover is what made the clicked control clickable (see\n // HoverRevealTracker; Cisco's subnav flyout, Sentry's \"Widget actions\" under\n // the widget title, card and row icons, nested menus), record those hovers,\n // outermost first, just before the click; otherwise replay's click never\n // converges.\n _maybeEmitHoverRevealBeforeClick(clickAction, clickTarget, previousUserHover) {\n if (!this._hoverRevealTracker)\n return false;\n if (previousUserHover && isInsideScope(previousUserHover, clickTarget))\n return false;\n const steps = this._hoverRevealTracker.hoverStepsFor(clickTarget, clickAction.selector, this.state.testIdAttributeName);\n if (!steps.length)\n return false;\n if (previousUserHover && steps.some((step) => isInsideScope(step.trigger, previousUserHover)))\n return true;\n const clickTs = parseInt(clickAction.timestamp, 10);\n steps.forEach(({ selector }, i) => {\n const hoverAction = {\n name: \"hover\",\n selector,\n signals: [],\n timestamp: Number.isFinite(clickTs) ? (clickTs - steps.length + i).toString() : clickAction.timestamp\n };\n if (this._delegate.recordAction)\n void this._delegate.recordAction(hoverAction).catch(() => {\n });\n });\n return true;\n }\n // Skyramp (SKYR-3966): aria-expanded of the element a click selector\n // targets, or undefined when it has none (or the selector does not resolve).\n expandedStateOf(selector) {\n const element = this._resolveActionTarget(selector);\n return element ? this.injectedScript.utils.getAriaExpanded(element) : void 0;\n }\n // The clicked element. A position click on the page (the Drag-and-drop\n // tool records `body` + position) resolves to the element pressed there.\n _clickTarget(action) {\n var _a, _b, _c;\n const target = this._resolveActionTarget(action.selector);\n if (!target || !action.position || target !== this.document.body && target !== this.document.documentElement)\n return target;\n const box = target.getBoundingClientRect();\n const x = box.left + action.position.x, y = box.top + action.position.y;\n return (_c = (_b = (_a = this._hoverRevealTracker) == null ? void 0 : _a.pressedAt(x, y)) != null ? _b : deepElementFromPoint(this.document, x, y)) != null ? _c : target;\n }\n _resolveActionTarget(selector) {\n try {\n return this.injectedScript.querySelectorAll(this.injectedScript.parseSelector(selector), this.document)[0];\n } catch {\n return void 0;\n }\n }\n recordAction(action) {\n ++this._recordedActionCount;\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};\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 isToolSurface(element) {\n if (!element || element === element.ownerDocument.body || element === element.ownerDocument.documentElement)\n return true;\n return !!element.closest(\"canvas, embed, object, [data-pw-pdf-viewer], [data-pw-pdf-text-layer]\");\n}\nfunction isTypingTarget(element) {\n if (!element)\n return false;\n if (element.nodeName === \"TEXTAREA\" || element.nodeName === \"SELECT\" || element.isContentEditable)\n return true;\n if (element.nodeName !== \"INPUT\")\n return false;\n return ![\"checkbox\", \"radio\", \"range\", \"file\", \"button\", \"submit\", \"reset\", \"image\"].includes(element.type.toLowerCase());\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 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 const win = this._recorder.document.defaultView;\n if (win.top !== win) {\n state.actionPoint = void 0;\n }\n try {\n this._recorder.setUIState(state, this);\n this._lastStateJSON = stringifiedState;\n } catch (e) {\n console.error(e);\n }\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";
|