tabwright 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (599) hide show
  1. package/LICENSE +21 -0
  2. package/bin.js +4 -0
  3. package/dist/a11y-client.js +204 -0
  4. package/dist/agent-skills/tabwright/SKILL.md +119 -0
  5. package/dist/aria-snapshot.d.ts +262 -0
  6. package/dist/aria-snapshot.d.ts.map +1 -0
  7. package/dist/aria-snapshot.js +1256 -0
  8. package/dist/aria-snapshot.js.map +1 -0
  9. package/dist/aria-snapshot.test.d.ts +2 -0
  10. package/dist/aria-snapshot.test.d.ts.map +1 -0
  11. package/dist/aria-snapshot.test.js +270 -0
  12. package/dist/aria-snapshot.test.js.map +1 -0
  13. package/dist/aria-snapshot.unit.test.d.ts +2 -0
  14. package/dist/aria-snapshot.unit.test.d.ts.map +1 -0
  15. package/dist/aria-snapshot.unit.test.js +542 -0
  16. package/dist/aria-snapshot.unit.test.js.map +1 -0
  17. package/dist/assets/cursors/screen-studio/pointer-macos-tahoe-data-url.d.ts +5 -0
  18. package/dist/assets/cursors/screen-studio/pointer-macos-tahoe-data-url.d.ts.map +1 -0
  19. package/dist/assets/cursors/screen-studio/pointer-macos-tahoe-data-url.js +5 -0
  20. package/dist/assets/cursors/screen-studio/pointer-macos-tahoe-data-url.js.map +1 -0
  21. package/dist/bippy.js +894 -0
  22. package/dist/browser-config.d.ts +13 -0
  23. package/dist/browser-config.d.ts.map +1 -0
  24. package/dist/browser-config.js +126 -0
  25. package/dist/browser-config.js.map +1 -0
  26. package/dist/browser-install.d.ts +16 -0
  27. package/dist/browser-install.d.ts.map +1 -0
  28. package/dist/browser-install.js +238 -0
  29. package/dist/browser-install.js.map +1 -0
  30. package/dist/browser-launch.d.ts +17 -0
  31. package/dist/browser-launch.d.ts.map +1 -0
  32. package/dist/browser-launch.js +44 -0
  33. package/dist/browser-launch.js.map +1 -0
  34. package/dist/builtin-capabilities.d.ts +28 -0
  35. package/dist/builtin-capabilities.d.ts.map +1 -0
  36. package/dist/builtin-capabilities.js +2673 -0
  37. package/dist/builtin-capabilities.js.map +1 -0
  38. package/dist/capability-agent-skill.d.ts +60 -0
  39. package/dist/capability-agent-skill.d.ts.map +1 -0
  40. package/dist/capability-agent-skill.js +225 -0
  41. package/dist/capability-agent-skill.js.map +1 -0
  42. package/dist/capability-auth-state.d.ts +34 -0
  43. package/dist/capability-auth-state.d.ts.map +1 -0
  44. package/dist/capability-auth-state.js +157 -0
  45. package/dist/capability-auth-state.js.map +1 -0
  46. package/dist/capability-auth.d.ts +36 -0
  47. package/dist/capability-auth.d.ts.map +1 -0
  48. package/dist/capability-auth.js +198 -0
  49. package/dist/capability-auth.js.map +1 -0
  50. package/dist/capability-cli-confirmation.test.d.ts +2 -0
  51. package/dist/capability-cli-confirmation.test.d.ts.map +1 -0
  52. package/dist/capability-cli-confirmation.test.js +79 -0
  53. package/dist/capability-cli-confirmation.test.js.map +1 -0
  54. package/dist/capability-options.d.ts +20 -0
  55. package/dist/capability-options.d.ts.map +1 -0
  56. package/dist/capability-options.js +31 -0
  57. package/dist/capability-options.js.map +1 -0
  58. package/dist/capability-options.test.d.ts +2 -0
  59. package/dist/capability-options.test.d.ts.map +1 -0
  60. package/dist/capability-options.test.js +81 -0
  61. package/dist/capability-options.test.js.map +1 -0
  62. package/dist/capability-package.d.ts +27 -0
  63. package/dist/capability-package.d.ts.map +1 -0
  64. package/dist/capability-package.js +444 -0
  65. package/dist/capability-package.js.map +1 -0
  66. package/dist/capability-package.test.d.ts +2 -0
  67. package/dist/capability-package.test.d.ts.map +1 -0
  68. package/dist/capability-package.test.js +179 -0
  69. package/dist/capability-package.test.js.map +1 -0
  70. package/dist/capability-registry.d.ts +262 -0
  71. package/dist/capability-registry.d.ts.map +1 -0
  72. package/dist/capability-registry.js +931 -0
  73. package/dist/capability-registry.js.map +1 -0
  74. package/dist/capability-registry.test.d.ts +2 -0
  75. package/dist/capability-registry.test.d.ts.map +1 -0
  76. package/dist/capability-registry.test.js +1683 -0
  77. package/dist/capability-registry.test.js.map +1 -0
  78. package/dist/capability-runner.d.ts +99 -0
  79. package/dist/capability-runner.d.ts.map +1 -0
  80. package/dist/capability-runner.js +608 -0
  81. package/dist/capability-runner.js.map +1 -0
  82. package/dist/capability-search-relevance.test.d.ts +2 -0
  83. package/dist/capability-search-relevance.test.d.ts.map +1 -0
  84. package/dist/capability-search-relevance.test.js +67 -0
  85. package/dist/capability-search-relevance.test.js.map +1 -0
  86. package/dist/capability-studio.d.ts +11 -0
  87. package/dist/capability-studio.d.ts.map +1 -0
  88. package/dist/capability-studio.js +593 -0
  89. package/dist/capability-studio.js.map +1 -0
  90. package/dist/capability-studio.test.d.ts +2 -0
  91. package/dist/capability-studio.test.d.ts.map +1 -0
  92. package/dist/capability-studio.test.js +41 -0
  93. package/dist/capability-studio.test.js.map +1 -0
  94. package/dist/cdp-log.d.ts +19 -0
  95. package/dist/cdp-log.d.ts.map +1 -0
  96. package/dist/cdp-log.js +83 -0
  97. package/dist/cdp-log.js.map +1 -0
  98. package/dist/cdp-log.test.d.ts +2 -0
  99. package/dist/cdp-log.test.d.ts.map +1 -0
  100. package/dist/cdp-log.test.js +109 -0
  101. package/dist/cdp-log.test.js.map +1 -0
  102. package/dist/cdp-relay.d.ts +19 -0
  103. package/dist/cdp-relay.d.ts.map +1 -0
  104. package/dist/cdp-relay.js +2375 -0
  105. package/dist/cdp-relay.js.map +1 -0
  106. package/dist/cdp-session.d.ts +37 -0
  107. package/dist/cdp-session.d.ts.map +1 -0
  108. package/dist/cdp-session.js +36 -0
  109. package/dist/cdp-session.js.map +1 -0
  110. package/dist/cdp-types.d.ts +65 -0
  111. package/dist/cdp-types.d.ts.map +1 -0
  112. package/dist/cdp-types.js +91 -0
  113. package/dist/cdp-types.js.map +1 -0
  114. package/dist/channel-owner-inspect.test.d.ts +2 -0
  115. package/dist/channel-owner-inspect.test.d.ts.map +1 -0
  116. package/dist/channel-owner-inspect.test.js +75 -0
  117. package/dist/channel-owner-inspect.test.js.map +1 -0
  118. package/dist/chrome-discovery.d.ts +65 -0
  119. package/dist/chrome-discovery.d.ts.map +1 -0
  120. package/dist/chrome-discovery.js +173 -0
  121. package/dist/chrome-discovery.js.map +1 -0
  122. package/dist/chrome-discovery.test.d.ts +2 -0
  123. package/dist/chrome-discovery.test.d.ts.map +1 -0
  124. package/dist/chrome-discovery.test.js +41 -0
  125. package/dist/chrome-discovery.test.js.map +1 -0
  126. package/dist/clean-html.d.ts +11 -0
  127. package/dist/clean-html.d.ts.map +1 -0
  128. package/dist/clean-html.js +106 -0
  129. package/dist/clean-html.js.map +1 -0
  130. package/dist/cli-help.test.d.ts +2 -0
  131. package/dist/cli-help.test.d.ts.map +1 -0
  132. package/dist/cli-help.test.js +98 -0
  133. package/dist/cli-help.test.js.map +1 -0
  134. package/dist/cli.d.ts +3 -0
  135. package/dist/cli.d.ts.map +1 -0
  136. package/dist/cli.js +2709 -0
  137. package/dist/cli.js.map +1 -0
  138. package/dist/cloud-client.d.ts +56 -0
  139. package/dist/cloud-client.d.ts.map +1 -0
  140. package/dist/cloud-client.js +127 -0
  141. package/dist/cloud-client.js.map +1 -0
  142. package/dist/create-logger.d.ts +9 -0
  143. package/dist/create-logger.d.ts.map +1 -0
  144. package/dist/create-logger.js +27 -0
  145. package/dist/create-logger.js.map +1 -0
  146. package/dist/debugger-api.md +458 -0
  147. package/dist/debugger-examples-types.d.ts +24 -0
  148. package/dist/debugger-examples-types.d.ts.map +1 -0
  149. package/dist/debugger-examples-types.js +2 -0
  150. package/dist/debugger-examples-types.js.map +1 -0
  151. package/dist/debugger-examples.d.ts +6 -0
  152. package/dist/debugger-examples.d.ts.map +1 -0
  153. package/dist/debugger-examples.js +53 -0
  154. package/dist/debugger-examples.js.map +1 -0
  155. package/dist/debugger.d.ts +381 -0
  156. package/dist/debugger.d.ts.map +1 -0
  157. package/dist/debugger.js +630 -0
  158. package/dist/debugger.js.map +1 -0
  159. package/dist/diff-utils.d.ts +20 -0
  160. package/dist/diff-utils.d.ts.map +1 -0
  161. package/dist/diff-utils.js +50 -0
  162. package/dist/diff-utils.js.map +1 -0
  163. package/dist/diff-utils.test.d.ts +2 -0
  164. package/dist/diff-utils.test.d.ts.map +1 -0
  165. package/dist/diff-utils.test.js +137 -0
  166. package/dist/diff-utils.test.js.map +1 -0
  167. package/dist/doctor.d.ts +44 -0
  168. package/dist/doctor.d.ts.map +1 -0
  169. package/dist/doctor.js +246 -0
  170. package/dist/doctor.js.map +1 -0
  171. package/dist/doctor.test.d.ts +2 -0
  172. package/dist/doctor.test.d.ts.map +1 -0
  173. package/dist/doctor.test.js +208 -0
  174. package/dist/doctor.test.js.map +1 -0
  175. package/dist/editor-api.md +374 -0
  176. package/dist/editor-examples.d.ts +11 -0
  177. package/dist/editor-examples.d.ts.map +1 -0
  178. package/dist/editor-examples.js +124 -0
  179. package/dist/editor-examples.js.map +1 -0
  180. package/dist/editor.d.ts +203 -0
  181. package/dist/editor.d.ts.map +1 -0
  182. package/dist/editor.js +363 -0
  183. package/dist/editor.js.map +1 -0
  184. package/dist/env-compat.d.ts +2 -0
  185. package/dist/env-compat.d.ts.map +1 -0
  186. package/dist/env-compat.js +27 -0
  187. package/dist/env-compat.js.map +1 -0
  188. package/dist/executor.d.ts +229 -0
  189. package/dist/executor.d.ts.map +1 -0
  190. package/dist/executor.js +1465 -0
  191. package/dist/executor.js.map +1 -0
  192. package/dist/executor.unit.test.d.ts +2 -0
  193. package/dist/executor.unit.test.d.ts.map +1 -0
  194. package/dist/executor.unit.test.js +143 -0
  195. package/dist/executor.unit.test.js.map +1 -0
  196. package/dist/extension/_locales/en/messages.json +278 -0
  197. package/dist/extension/_locales/zh_CN/messages.json +256 -0
  198. package/dist/extension/assets/options-CYH96Lcx.css +1349 -0
  199. package/dist/extension/background.js +3994 -0
  200. package/dist/extension/icons/SolarCursorSquareBold.png +0 -0
  201. package/dist/extension/icons/icon-black-128.png +0 -0
  202. package/dist/extension/icons/icon-black-16.png +0 -0
  203. package/dist/extension/icons/icon-black-32.png +0 -0
  204. package/dist/extension/icons/icon-black-48.png +0 -0
  205. package/dist/extension/icons/icon-gray-128.png +0 -0
  206. package/dist/extension/icons/icon-gray-16.png +0 -0
  207. package/dist/extension/icons/icon-gray-32.png +0 -0
  208. package/dist/extension/icons/icon-gray-48.png +0 -0
  209. package/dist/extension/icons/icon-green-128.png +0 -0
  210. package/dist/extension/icons/icon-green-16.png +0 -0
  211. package/dist/extension/icons/icon-green-32.png +0 -0
  212. package/dist/extension/icons/icon-green-48.png +0 -0
  213. package/dist/extension/icons/tabwright-icon-black.png +0 -0
  214. package/dist/extension/icons/tabwright-icon-gray-disabled.png +0 -0
  215. package/dist/extension/icons/tabwright-icon-gray-disabled.svg +1 -0
  216. package/dist/extension/manifest.json +54 -0
  217. package/dist/extension/options.js +16145 -0
  218. package/dist/extension/rrweb-recorder.js +12952 -0
  219. package/dist/extension/src/options.html +165 -0
  220. package/dist/extension/src/prism-bash.min.js +1 -0
  221. package/dist/extension/src/prism.min.js +1 -0
  222. package/dist/extension/src/welcome.html +396 -0
  223. package/dist/extension-connection.test.d.ts +2 -0
  224. package/dist/extension-connection.test.d.ts.map +1 -0
  225. package/dist/extension-connection.test.js +708 -0
  226. package/dist/extension-connection.test.js.map +1 -0
  227. package/dist/extension-icon-warning.test.d.ts +2 -0
  228. package/dist/extension-icon-warning.test.d.ts.map +1 -0
  229. package/dist/extension-icon-warning.test.js +33 -0
  230. package/dist/extension-icon-warning.test.js.map +1 -0
  231. package/dist/ghost-browser.d.ts +62 -0
  232. package/dist/ghost-browser.d.ts.map +1 -0
  233. package/dist/ghost-browser.js +107 -0
  234. package/dist/ghost-browser.js.map +1 -0
  235. package/dist/ghost-cursor-client.js +374 -0
  236. package/dist/ghost-cursor-controller.d.ts +46 -0
  237. package/dist/ghost-cursor-controller.d.ts.map +1 -0
  238. package/dist/ghost-cursor-controller.js +98 -0
  239. package/dist/ghost-cursor-controller.js.map +1 -0
  240. package/dist/ghost-cursor.d.ts +27 -0
  241. package/dist/ghost-cursor.d.ts.map +1 -0
  242. package/dist/ghost-cursor.js +79 -0
  243. package/dist/ghost-cursor.js.map +1 -0
  244. package/dist/htmlrewrite.d.ts +8 -0
  245. package/dist/htmlrewrite.d.ts.map +1 -0
  246. package/dist/htmlrewrite.js +339 -0
  247. package/dist/htmlrewrite.js.map +1 -0
  248. package/dist/htmlrewrite.test.d.ts +2 -0
  249. package/dist/htmlrewrite.test.d.ts.map +1 -0
  250. package/dist/htmlrewrite.test.js +7975 -0
  251. package/dist/htmlrewrite.test.js.map +1 -0
  252. package/dist/index.d.ts +20 -0
  253. package/dist/index.d.ts.map +1 -0
  254. package/dist/index.js +11 -0
  255. package/dist/index.js.map +1 -0
  256. package/dist/kill-port.d.ts +29 -0
  257. package/dist/kill-port.d.ts.map +1 -0
  258. package/dist/kill-port.js +190 -0
  259. package/dist/kill-port.js.map +1 -0
  260. package/dist/kitty-graphics.d.ts +22 -0
  261. package/dist/kitty-graphics.d.ts.map +1 -0
  262. package/dist/kitty-graphics.js +71 -0
  263. package/dist/kitty-graphics.js.map +1 -0
  264. package/dist/kitty-graphics.test.d.ts +2 -0
  265. package/dist/kitty-graphics.test.d.ts.map +1 -0
  266. package/dist/kitty-graphics.test.js +125 -0
  267. package/dist/kitty-graphics.test.js.map +1 -0
  268. package/dist/locator-selector.test.d.ts +2 -0
  269. package/dist/locator-selector.test.d.ts.map +1 -0
  270. package/dist/locator-selector.test.js +98 -0
  271. package/dist/locator-selector.test.js.map +1 -0
  272. package/dist/mcp-client.d.ts +20 -0
  273. package/dist/mcp-client.d.ts.map +1 -0
  274. package/dist/mcp-client.js +56 -0
  275. package/dist/mcp-client.js.map +1 -0
  276. package/dist/mcp.d.ts +6 -0
  277. package/dist/mcp.d.ts.map +1 -0
  278. package/dist/mcp.js +511 -0
  279. package/dist/mcp.js.map +1 -0
  280. package/dist/on-mouse-action.test.d.ts +2 -0
  281. package/dist/on-mouse-action.test.d.ts.map +1 -0
  282. package/dist/on-mouse-action.test.js +180 -0
  283. package/dist/on-mouse-action.test.js.map +1 -0
  284. package/dist/options-page.test.d.ts +2 -0
  285. package/dist/options-page.test.d.ts.map +1 -0
  286. package/dist/options-page.test.js +773 -0
  287. package/dist/options-page.test.js.map +1 -0
  288. package/dist/package-paths.d.ts +4 -0
  289. package/dist/package-paths.d.ts.map +1 -0
  290. package/dist/package-paths.js +30 -0
  291. package/dist/package-paths.js.map +1 -0
  292. package/dist/page-markdown.d.ts +40 -0
  293. package/dist/page-markdown.d.ts.map +1 -0
  294. package/dist/page-markdown.js +181 -0
  295. package/dist/page-markdown.js.map +1 -0
  296. package/dist/performance-examples.d.ts +5 -0
  297. package/dist/performance-examples.d.ts.map +1 -0
  298. package/dist/performance-examples.js +112 -0
  299. package/dist/performance-examples.js.map +1 -0
  300. package/dist/performance-profiling.md +417 -0
  301. package/dist/playwright-import.d.ts +19 -0
  302. package/dist/playwright-import.d.ts.map +1 -0
  303. package/dist/playwright-import.js +40 -0
  304. package/dist/playwright-import.js.map +1 -0
  305. package/dist/popup-relocation.test.d.ts +7 -0
  306. package/dist/popup-relocation.test.d.ts.map +1 -0
  307. package/dist/popup-relocation.test.js +139 -0
  308. package/dist/popup-relocation.test.js.map +1 -0
  309. package/dist/product-paths.d.ts +9 -0
  310. package/dist/product-paths.d.ts.map +1 -0
  311. package/dist/product-paths.js +24 -0
  312. package/dist/product-paths.js.map +1 -0
  313. package/dist/product-paths.test.d.ts +2 -0
  314. package/dist/product-paths.test.d.ts.map +1 -0
  315. package/dist/product-paths.test.js +39 -0
  316. package/dist/product-paths.test.js.map +1 -0
  317. package/dist/prompt.md +1004 -0
  318. package/dist/protocol.d.ts +248 -0
  319. package/dist/protocol.d.ts.map +1 -0
  320. package/dist/protocol.js +59 -0
  321. package/dist/protocol.js.map +1 -0
  322. package/dist/protocol.test.d.ts +2 -0
  323. package/dist/protocol.test.d.ts.map +1 -0
  324. package/dist/protocol.test.js +31 -0
  325. package/dist/protocol.test.js.map +1 -0
  326. package/dist/react-source.d.ts +57 -0
  327. package/dist/react-source.d.ts.map +1 -0
  328. package/dist/react-source.js +254 -0
  329. package/dist/react-source.js.map +1 -0
  330. package/dist/readability.js +1674 -0
  331. package/dist/relay-client.d.ts +104 -0
  332. package/dist/relay-client.d.ts.map +1 -0
  333. package/dist/relay-client.js +426 -0
  334. package/dist/relay-client.js.map +1 -0
  335. package/dist/relay-client.test.d.ts +2 -0
  336. package/dist/relay-client.test.d.ts.map +1 -0
  337. package/dist/relay-client.test.js +305 -0
  338. package/dist/relay-client.test.js.map +1 -0
  339. package/dist/relay-core.test.d.ts +2 -0
  340. package/dist/relay-core.test.d.ts.map +1 -0
  341. package/dist/relay-core.test.js +1427 -0
  342. package/dist/relay-core.test.js.map +1 -0
  343. package/dist/relay-navigation.test.d.ts +2 -0
  344. package/dist/relay-navigation.test.d.ts.map +1 -0
  345. package/dist/relay-navigation.test.js +661 -0
  346. package/dist/relay-navigation.test.js.map +1 -0
  347. package/dist/relay-session.test.d.ts +2 -0
  348. package/dist/relay-session.test.d.ts.map +1 -0
  349. package/dist/relay-session.test.js +1121 -0
  350. package/dist/relay-session.test.js.map +1 -0
  351. package/dist/relay-state.d.ts +178 -0
  352. package/dist/relay-state.d.ts.map +1 -0
  353. package/dist/relay-state.js +346 -0
  354. package/dist/relay-state.js.map +1 -0
  355. package/dist/relay-state.test.d.ts +2 -0
  356. package/dist/relay-state.test.d.ts.map +1 -0
  357. package/dist/relay-state.test.js +594 -0
  358. package/dist/relay-state.test.js.map +1 -0
  359. package/dist/replay-ai-index.d.ts +99 -0
  360. package/dist/replay-ai-index.d.ts.map +1 -0
  361. package/dist/replay-ai-index.js +677 -0
  362. package/dist/replay-ai-index.js.map +1 -0
  363. package/dist/replay-ai-index.test.d.ts +2 -0
  364. package/dist/replay-ai-index.test.d.ts.map +1 -0
  365. package/dist/replay-ai-index.test.js +255 -0
  366. package/dist/replay-ai-index.test.js.map +1 -0
  367. package/dist/replay-cli.test.d.ts +2 -0
  368. package/dist/replay-cli.test.d.ts.map +1 -0
  369. package/dist/replay-cli.test.js +275 -0
  370. package/dist/replay-cli.test.js.map +1 -0
  371. package/dist/replay-eval.d.ts +44 -0
  372. package/dist/replay-eval.d.ts.map +1 -0
  373. package/dist/replay-eval.js +902 -0
  374. package/dist/replay-eval.js.map +1 -0
  375. package/dist/replay-handoff.d.ts +29 -0
  376. package/dist/replay-handoff.d.ts.map +1 -0
  377. package/dist/replay-handoff.js +83 -0
  378. package/dist/replay-handoff.js.map +1 -0
  379. package/dist/replay-handoff.test.d.ts +2 -0
  380. package/dist/replay-handoff.test.d.ts.map +1 -0
  381. package/dist/replay-handoff.test.js +123 -0
  382. package/dist/replay-handoff.test.js.map +1 -0
  383. package/dist/replay-workflow-compiler.d.ts +44 -0
  384. package/dist/replay-workflow-compiler.d.ts.map +1 -0
  385. package/dist/replay-workflow-compiler.js +601 -0
  386. package/dist/replay-workflow-compiler.js.map +1 -0
  387. package/dist/replay-workflow-compiler.test.d.ts +2 -0
  388. package/dist/replay-workflow-compiler.test.d.ts.map +1 -0
  389. package/dist/replay-workflow-compiler.test.js +223 -0
  390. package/dist/replay-workflow-compiler.test.js.map +1 -0
  391. package/dist/rrweb-recording-relay.d.ts +56 -0
  392. package/dist/rrweb-recording-relay.d.ts.map +1 -0
  393. package/dist/rrweb-recording-relay.js +299 -0
  394. package/dist/rrweb-recording-relay.js.map +1 -0
  395. package/dist/rrweb-recording-relay.test.d.ts +2 -0
  396. package/dist/rrweb-recording-relay.test.d.ts.map +1 -0
  397. package/dist/rrweb-recording-relay.test.js +85 -0
  398. package/dist/rrweb-recording-relay.test.js.map +1 -0
  399. package/dist/rrweb-recording.d.ts +104 -0
  400. package/dist/rrweb-recording.d.ts.map +1 -0
  401. package/dist/rrweb-recording.js +180 -0
  402. package/dist/rrweb-recording.js.map +1 -0
  403. package/dist/scoped-fs.d.ts +95 -0
  404. package/dist/scoped-fs.d.ts.map +1 -0
  405. package/dist/scoped-fs.js +358 -0
  406. package/dist/scoped-fs.js.map +1 -0
  407. package/dist/scoped-fs.test.d.ts +5 -0
  408. package/dist/scoped-fs.test.d.ts.map +1 -0
  409. package/dist/scoped-fs.test.js +73 -0
  410. package/dist/scoped-fs.test.js.map +1 -0
  411. package/dist/selector-generator.js +7378 -0
  412. package/dist/skill-stub.test.d.ts +2 -0
  413. package/dist/skill-stub.test.d.ts.map +1 -0
  414. package/dist/skill-stub.test.js +22 -0
  415. package/dist/skill-stub.test.js.map +1 -0
  416. package/dist/snapshot-tools.test.d.ts +2 -0
  417. package/dist/snapshot-tools.test.d.ts.map +1 -0
  418. package/dist/snapshot-tools.test.js +856 -0
  419. package/dist/snapshot-tools.test.js.map +1 -0
  420. package/dist/start-relay-server.d.ts +6 -0
  421. package/dist/start-relay-server.d.ts.map +1 -0
  422. package/dist/start-relay-server.js +57 -0
  423. package/dist/start-relay-server.js.map +1 -0
  424. package/dist/styles-api.md +124 -0
  425. package/dist/styles-examples.d.ts +8 -0
  426. package/dist/styles-examples.d.ts.map +1 -0
  427. package/dist/styles-examples.js +64 -0
  428. package/dist/styles-examples.js.map +1 -0
  429. package/dist/styles.d.ts +27 -0
  430. package/dist/styles.d.ts.map +1 -0
  431. package/dist/styles.js +231 -0
  432. package/dist/styles.js.map +1 -0
  433. package/dist/tabwright-agent-skill.d.ts +30 -0
  434. package/dist/tabwright-agent-skill.d.ts.map +1 -0
  435. package/dist/tabwright-agent-skill.js +74 -0
  436. package/dist/tabwright-agent-skill.js.map +1 -0
  437. package/dist/tabwright-agent-skill.test.d.ts +2 -0
  438. package/dist/tabwright-agent-skill.test.d.ts.map +1 -0
  439. package/dist/tabwright-agent-skill.test.js +51 -0
  440. package/dist/tabwright-agent-skill.test.js.map +1 -0
  441. package/dist/test-declarations.d.ts +13 -0
  442. package/dist/test-declarations.d.ts.map +1 -0
  443. package/dist/test-declarations.js +2 -0
  444. package/dist/test-declarations.js.map +1 -0
  445. package/dist/test-utils.d.ts +69 -0
  446. package/dist/test-utils.d.ts.map +1 -0
  447. package/dist/test-utils.js +448 -0
  448. package/dist/test-utils.js.map +1 -0
  449. package/dist/test-utils.test.d.ts +2 -0
  450. package/dist/test-utils.test.d.ts.map +1 -0
  451. package/dist/test-utils.test.js +55 -0
  452. package/dist/test-utils.test.js.map +1 -0
  453. package/dist/utils.d.ts +26 -0
  454. package/dist/utils.d.ts.map +1 -0
  455. package/dist/utils.js +60 -0
  456. package/dist/utils.js.map +1 -0
  457. package/dist/wait-for-page-load.d.ts +16 -0
  458. package/dist/wait-for-page-load.d.ts.map +1 -0
  459. package/dist/wait-for-page-load.js +146 -0
  460. package/dist/wait-for-page-load.js.map +1 -0
  461. package/dist/workflow-capability.d.ts +72 -0
  462. package/dist/workflow-capability.d.ts.map +1 -0
  463. package/dist/workflow-capability.js +316 -0
  464. package/dist/workflow-capability.js.map +1 -0
  465. package/dist/workflow-script-flow.test.d.ts +2 -0
  466. package/dist/workflow-script-flow.test.d.ts.map +1 -0
  467. package/dist/workflow-script-flow.test.js +285 -0
  468. package/dist/workflow-script-flow.test.js.map +1 -0
  469. package/package.json +80 -0
  470. package/src/__snapshots__/x.com.processed.html +1003 -0
  471. package/src/__snapshots__/x.com.processed.withStyles.html +2720 -0
  472. package/src/a11y-client.ts +241 -0
  473. package/src/agent-skills/conan-config/SKILL.md +141 -0
  474. package/src/agent-skills/conan-config/agents/openai.yaml +4 -0
  475. package/src/aria-snapshot.test.ts +311 -0
  476. package/src/aria-snapshot.ts +1681 -0
  477. package/src/aria-snapshot.unit.test.ts +615 -0
  478. package/src/aria-snapshots/github-interactive.txt +193 -0
  479. package/src/aria-snapshots/github-raw.txt +309 -0
  480. package/src/aria-snapshots/hackernews-interactive.txt +477 -0
  481. package/src/aria-snapshots/hackernews-raw.txt +428 -0
  482. package/src/aria-snapshots/prosemirror-interactive.txt +66 -0
  483. package/src/aria-snapshots/prosemirror-raw.txt +145 -0
  484. package/src/assets/aria-labels-hacker-news.png +0 -0
  485. package/src/assets/aria-labels-old-reddit.png +0 -0
  486. package/src/assets/cursors/screen-studio/pointer-macos-tahoe-data-url.ts +5 -0
  487. package/src/assets/cursors/screen-studio/pointer-macos-tahoe.svg +18 -0
  488. package/src/assets/x.com.html +32946 -0
  489. package/src/browser-config.ts +179 -0
  490. package/src/browser-install.ts +284 -0
  491. package/src/browser-launch.ts +75 -0
  492. package/src/builtin-capabilities.ts +2765 -0
  493. package/src/capability-agent-skill.ts +305 -0
  494. package/src/capability-auth-state.ts +197 -0
  495. package/src/capability-auth.ts +249 -0
  496. package/src/capability-cli-confirmation.test.ts +90 -0
  497. package/src/capability-options.test.ts +89 -0
  498. package/src/capability-options.ts +57 -0
  499. package/src/capability-package.test.ts +204 -0
  500. package/src/capability-package.ts +555 -0
  501. package/src/capability-registry.test.ts +1905 -0
  502. package/src/capability-registry.ts +1234 -0
  503. package/src/capability-runner.ts +801 -0
  504. package/src/capability-search-relevance.test.ts +73 -0
  505. package/src/capability-studio.test.ts +45 -0
  506. package/src/capability-studio.ts +641 -0
  507. package/src/cdp-log.test.ts +131 -0
  508. package/src/cdp-log.ts +112 -0
  509. package/src/cdp-relay.ts +2944 -0
  510. package/src/cdp-session.ts +77 -0
  511. package/src/cdp-types.ts +158 -0
  512. package/src/channel-owner-inspect.test.ts +94 -0
  513. package/src/chrome-discovery.test.ts +50 -0
  514. package/src/chrome-discovery.ts +220 -0
  515. package/src/clean-html.ts +141 -0
  516. package/src/cli-help.test.ts +123 -0
  517. package/src/cli.ts +3181 -0
  518. package/src/cloud-client.ts +179 -0
  519. package/src/create-logger.ts +38 -0
  520. package/src/debugger-examples-types.ts +16 -0
  521. package/src/debugger-examples.ts +66 -0
  522. package/src/debugger.ts +708 -0
  523. package/src/diff-utils.test.ts +152 -0
  524. package/src/diff-utils.ts +70 -0
  525. package/src/doctor.test.ts +244 -0
  526. package/src/doctor.ts +321 -0
  527. package/src/editor-examples.ts +158 -0
  528. package/src/editor.ts +426 -0
  529. package/src/env-compat.ts +28 -0
  530. package/src/executor.ts +1824 -0
  531. package/src/executor.unit.test.ts +168 -0
  532. package/src/extension-connection.test.ts +855 -0
  533. package/src/extension-icon-warning.test.ts +45 -0
  534. package/src/ghost-browser.ts +149 -0
  535. package/src/ghost-cursor-client.ts +494 -0
  536. package/src/ghost-cursor-controller.ts +129 -0
  537. package/src/ghost-cursor.ts +123 -0
  538. package/src/htmlrewrite.test.ts +8008 -0
  539. package/src/htmlrewrite.ts +390 -0
  540. package/src/index.ts +53 -0
  541. package/src/kill-port.ts +219 -0
  542. package/src/kitty-graphics.test.ts +139 -0
  543. package/src/kitty-graphics.ts +79 -0
  544. package/src/locator-selector.test.ts +119 -0
  545. package/src/mcp-client.ts +78 -0
  546. package/src/mcp.ts +629 -0
  547. package/src/on-mouse-action.test.ts +226 -0
  548. package/src/options-page.test.ts +839 -0
  549. package/src/package-paths.ts +38 -0
  550. package/src/page-markdown.ts +240 -0
  551. package/src/performance-examples.ts +186 -0
  552. package/src/playwright-import.ts +59 -0
  553. package/src/popup-relocation.test.ts +163 -0
  554. package/src/product-paths.test.ts +47 -0
  555. package/src/product-paths.ts +28 -0
  556. package/src/protocol.test.ts +52 -0
  557. package/src/protocol.ts +351 -0
  558. package/src/react-source.ts +379 -0
  559. package/src/relay-client.test.ts +375 -0
  560. package/src/relay-client.ts +572 -0
  561. package/src/relay-core.test.ts +1603 -0
  562. package/src/relay-navigation.test.ts +790 -0
  563. package/src/relay-session.test.ts +1375 -0
  564. package/src/relay-state.test.ts +729 -0
  565. package/src/relay-state.ts +561 -0
  566. package/src/replay-ai-index.test.ts +278 -0
  567. package/src/replay-ai-index.ts +872 -0
  568. package/src/replay-cli.test.ts +325 -0
  569. package/src/replay-eval.ts +1039 -0
  570. package/src/replay-handoff.test.ts +163 -0
  571. package/src/replay-handoff.ts +109 -0
  572. package/src/replay-workflow-compiler.test.ts +250 -0
  573. package/src/replay-workflow-compiler.ts +700 -0
  574. package/src/resource.md +409 -0
  575. package/src/rrweb-recording-relay.test.ts +99 -0
  576. package/src/rrweb-recording-relay.ts +400 -0
  577. package/src/rrweb-recording.ts +322 -0
  578. package/src/scoped-fs.test.ts +80 -0
  579. package/src/scoped-fs.ts +420 -0
  580. package/src/skill-stub.test.ts +24 -0
  581. package/src/skill.md +1413 -0
  582. package/src/snapshot-tools.test.ts +1017 -0
  583. package/src/snapshots/hacker-news-accessibility-full.md +81 -0
  584. package/src/snapshots/hacker-news-accessibility-interactive.md +43 -0
  585. package/src/snapshots/page-markdown-output.txt +16 -0
  586. package/src/snapshots/shadcn-ui-accessibility-full.md +201 -0
  587. package/src/snapshots/shadcn-ui-accessibility-interactive.md +109 -0
  588. package/src/start-relay-server.ts +69 -0
  589. package/src/styles-examples.ts +84 -0
  590. package/src/styles.ts +343 -0
  591. package/src/tabwright-agent-skill.test.ts +61 -0
  592. package/src/tabwright-agent-skill.ts +105 -0
  593. package/src/test-declarations.ts +13 -0
  594. package/src/test-utils.test.ts +70 -0
  595. package/src/test-utils.ts +566 -0
  596. package/src/utils.ts +78 -0
  597. package/src/wait-for-page-load.ts +198 -0
  598. package/src/workflow-capability.ts +417 -0
  599. package/src/workflow-script-flow.test.ts +388 -0
@@ -0,0 +1,38 @@
1
+ import fs from 'node:fs'
2
+ import path from 'node:path'
3
+ import { fileURLToPath } from 'node:url'
4
+ import { createRequire } from 'node:module'
5
+
6
+ const require = createRequire(import.meta.url)
7
+
8
+ export function getInstalledTabwrightPackageDir(): string {
9
+ const localPackageDir = path.resolve(path.dirname(fileURLToPath(import.meta.url)), '..')
10
+ const localPackageJsonPath = path.join(localPackageDir, 'package.json')
11
+ if (fs.existsSync(localPackageJsonPath)) {
12
+ return localPackageDir
13
+ }
14
+
15
+ const packageJsonPath = require.resolve('tabwright/package.json')
16
+ return path.dirname(packageJsonPath)
17
+ }
18
+
19
+ export function getBundledExtensionPath(): string {
20
+ const packageDir = getInstalledTabwrightPackageDir()
21
+ const candidates = [
22
+ path.join(packageDir, 'dist', 'extension'),
23
+ path.join(packageDir, '..', 'extension', 'dist'),
24
+ ]
25
+
26
+ for (const extensionPath of candidates) {
27
+ const manifestPath = path.join(extensionPath, 'manifest.json')
28
+ if (fs.existsSync(manifestPath)) {
29
+ return extensionPath
30
+ }
31
+ }
32
+
33
+ throw new Error(
34
+ `Bundled Tabwright extension not found under ${packageDir}. Rebuild or reinstall the tabwright package.`,
35
+ )
36
+ }
37
+
38
+ export const getInstalledPlaywriterPackageDir = getInstalledTabwrightPackageDir
@@ -0,0 +1,240 @@
1
+ /**
2
+ * Extract page content as markdown using Mozilla Readability.
3
+ *
4
+ * This utility injects the Readability library into the page and extracts
5
+ * the main content, similar to Firefox Reader View.
6
+ */
7
+
8
+ import fs from 'node:fs'
9
+ import path from 'node:path'
10
+ import { fileURLToPath } from 'node:url'
11
+ import type { Page } from '@xmorse/playwright-core'
12
+ import { createSmartDiff } from './diff-utils.js'
13
+
14
+ export interface PageMarkdownResult {
15
+ /** Extracted content as plain text (HTML tags stripped) */
16
+ content: string
17
+ /** Article title */
18
+ title: string | null
19
+ /** Article author/byline */
20
+ author: string | null
21
+ /** Article excerpt/description */
22
+ excerpt: string | null
23
+ /** Site name */
24
+ siteName: string | null
25
+ /** Content language */
26
+ lang: string | null
27
+ /** Published time */
28
+ publishedTime: string | null
29
+ /** Word count */
30
+ wordCount: number
31
+ }
32
+
33
+ export interface GetPageMarkdownOptions {
34
+ page: Page
35
+ /** String or regex to filter content (returns matching lines with context) */
36
+ search?: string | RegExp
37
+ /** Return diff since last call for this page */
38
+ showDiffSinceLastCall?: boolean
39
+ }
40
+
41
+ // Cache for the bundled readability code
42
+ let readabilityCode: string | null = null
43
+
44
+ function getReadabilityCode(): string {
45
+ if (readabilityCode) {
46
+ return readabilityCode
47
+ }
48
+ const currentDir = path.dirname(fileURLToPath(import.meta.url))
49
+ const readabilityPath = path.join(currentDir, '..', 'dist', 'readability.js')
50
+ readabilityCode = fs.readFileSync(readabilityPath, 'utf-8')
51
+ return readabilityCode
52
+ }
53
+
54
+ // Store last snapshots per page for diffing
55
+ const lastMarkdownSnapshots: WeakMap<Page, string> = new WeakMap()
56
+
57
+ function isRegExp(value: unknown): value is RegExp {
58
+ return (
59
+ typeof value === 'object' &&
60
+ value !== null &&
61
+ typeof (value as RegExp).test === 'function' &&
62
+ typeof (value as RegExp).exec === 'function'
63
+ )
64
+ }
65
+
66
+ /**
67
+ * Extract page content as markdown using Mozilla Readability.
68
+ *
69
+ * Injects Readability into the page if not already present, then extracts
70
+ * the main content. Returns plain text content (no HTML).
71
+ */
72
+ export async function getPageMarkdown(options: GetPageMarkdownOptions): Promise<string> {
73
+ const { page, search, showDiffSinceLastCall = !search } = options
74
+
75
+ // Check if readability is already injected
76
+ const hasReadability = await page.evaluate(() => !!(globalThis as any).__readability)
77
+
78
+ if (!hasReadability) {
79
+ const code = getReadabilityCode()
80
+ await page.evaluate(code)
81
+ }
82
+
83
+ // Extract content using Readability
84
+ const result = (await page.evaluate(() => {
85
+ const readability = (globalThis as any).__readability
86
+ if (!readability) {
87
+ throw new Error('Readability not loaded')
88
+ }
89
+
90
+ // Clone document to avoid modifying the original
91
+ const documentClone = document.cloneNode(true)
92
+
93
+ // Check if page is probably readable
94
+ if (!readability.isProbablyReaderable(documentClone)) {
95
+ return {
96
+ content: document.body?.innerText || '',
97
+ title: document.title || null,
98
+ author: null,
99
+ excerpt: null,
100
+ siteName: null,
101
+ lang: document.documentElement?.lang || null,
102
+ publishedTime: null,
103
+ wordCount: (document.body?.innerText || '').split(/\s+/).filter(Boolean).length,
104
+ _notReadable: true,
105
+ }
106
+ }
107
+
108
+ const article = new readability.Readability(documentClone).parse()
109
+
110
+ if (!article) {
111
+ return {
112
+ content: document.body?.innerText || '',
113
+ title: document.title || null,
114
+ author: null,
115
+ excerpt: null,
116
+ siteName: null,
117
+ lang: document.documentElement?.lang || null,
118
+ publishedTime: null,
119
+ wordCount: (document.body?.innerText || '').split(/\s+/).filter(Boolean).length,
120
+ _notReadable: true,
121
+ }
122
+ }
123
+
124
+ return {
125
+ content: article.textContent || '',
126
+ title: article.title || null,
127
+ author: article.byline || null,
128
+ excerpt: article.excerpt || null,
129
+ siteName: article.siteName || null,
130
+ lang: article.lang || null,
131
+ publishedTime: article.publishedTime || null,
132
+ wordCount: (article.textContent || '').split(/\s+/).filter(Boolean).length,
133
+ }
134
+ })) as PageMarkdownResult & { _notReadable?: boolean }
135
+
136
+ // Format output
137
+ const lines: string[] = []
138
+
139
+ if (result.title) {
140
+ lines.push(`# ${result.title}`)
141
+ lines.push('')
142
+ }
143
+
144
+ const metadata: string[] = []
145
+ if (result.author) {
146
+ metadata.push(`Author: ${result.author}`)
147
+ }
148
+ if (result.siteName) {
149
+ metadata.push(`Site: ${result.siteName}`)
150
+ }
151
+ if (result.publishedTime) {
152
+ metadata.push(`Published: ${result.publishedTime}`)
153
+ }
154
+ if (metadata.length > 0) {
155
+ lines.push(metadata.join(' | '))
156
+ lines.push('')
157
+ }
158
+
159
+ if (result.excerpt && result.excerpt !== result.content.slice(0, result.excerpt.length)) {
160
+ lines.push(`> ${result.excerpt}`)
161
+ lines.push('')
162
+ }
163
+
164
+ lines.push(result.content)
165
+
166
+ let markdown = lines.join('\n').trim()
167
+
168
+ // Sanitize to remove unpaired surrogates that break JSON encoding
169
+ markdown = markdown.toWellFormed?.() ?? markdown
170
+
171
+ // Store snapshot and handle diffing
172
+ const previousSnapshot = lastMarkdownSnapshots.get(page)
173
+ lastMarkdownSnapshots.set(page, markdown)
174
+
175
+ // Diff defaults off when search is provided, but agent can explicitly enable both
176
+ if (showDiffSinceLastCall && previousSnapshot) {
177
+ const diffResult = createSmartDiff({
178
+ oldContent: previousSnapshot,
179
+ newContent: markdown,
180
+ label: 'content',
181
+ })
182
+ if (diffResult.type === 'no-change') {
183
+ return 'No changes since last call. Use showDiffSinceLastCall: false to see full content.'
184
+ }
185
+ return diffResult.content
186
+ }
187
+
188
+ // Handle search
189
+ if (search) {
190
+ const contentLines = markdown.split('\n')
191
+ const matchIndices: number[] = []
192
+
193
+ for (let i = 0; i < contentLines.length; i++) {
194
+ const line = contentLines[i]
195
+ let isMatch = false
196
+ if (isRegExp(search)) {
197
+ isMatch = search.test(line)
198
+ } else {
199
+ isMatch = line.toLowerCase().includes(search.toLowerCase())
200
+ }
201
+
202
+ if (isMatch) {
203
+ matchIndices.push(i)
204
+ if (matchIndices.length >= 10) {
205
+ break
206
+ }
207
+ }
208
+ }
209
+
210
+ if (matchIndices.length === 0) {
211
+ return 'No matches found'
212
+ }
213
+
214
+ // Collect lines with 5 lines of context above and below each match
215
+ const CONTEXT_LINES = 5
216
+ const includedLines = new Set<number>()
217
+ for (const idx of matchIndices) {
218
+ const start = Math.max(0, idx - CONTEXT_LINES)
219
+ const end = Math.min(contentLines.length - 1, idx + CONTEXT_LINES)
220
+ for (let i = start; i <= end; i++) {
221
+ includedLines.add(i)
222
+ }
223
+ }
224
+
225
+ // Build result with separators between non-contiguous sections
226
+ const sortedIndices = [...includedLines].sort((a, b) => a - b)
227
+ const resultLines: string[] = []
228
+ for (let i = 0; i < sortedIndices.length; i++) {
229
+ const lineIdx = sortedIndices[i]
230
+ if (i > 0 && sortedIndices[i - 1] !== lineIdx - 1) {
231
+ resultLines.push('---')
232
+ }
233
+ resultLines.push(contentLines[lineIdx])
234
+ }
235
+
236
+ return resultLines.join('\n')
237
+ }
238
+
239
+ return markdown
240
+ }
@@ -0,0 +1,186 @@
1
+ // Example snippets for profiling website performance with Tabwright and CDP.
2
+
3
+ import { console, getCDPSession, page } from './debugger-examples-types.js'
4
+
5
+ type PerfMetrics = {
6
+ paints: Record<string, number>
7
+ lcp: number
8
+ cls: number
9
+ }
10
+
11
+ type ObservedPerfEntry = {
12
+ name: string
13
+ startTime: number
14
+ duration: number
15
+ hadRecentInput?: boolean
16
+ value?: number
17
+ interactionId?: number
18
+ }
19
+
20
+ type NavigationTimingEntry = {
21
+ responseStart: number
22
+ domContentLoadedEventEnd: number
23
+ loadEventEnd: number
24
+ }
25
+
26
+ type LongTaskEntry = {
27
+ startTime: number
28
+ duration: number
29
+ }
30
+
31
+ type EventTimingEntry = {
32
+ name: string
33
+ duration: number
34
+ interactionId: number
35
+ }
36
+
37
+ // Example: Collect navigation timing and basic web vitals from the current page
38
+ async function collectWebVitals() {
39
+ await page.evaluate(() => {
40
+ const metrics: PerfMetrics = {
41
+ paints: {},
42
+ lcp: 0,
43
+ cls: 0,
44
+ }
45
+
46
+ const perfGlobal = globalThis as typeof globalThis & {
47
+ __pwPerfMetrics?: PerfMetrics
48
+ }
49
+
50
+ perfGlobal.__pwPerfMetrics = metrics
51
+
52
+ new PerformanceObserver((list) => {
53
+ for (const entry of list.getEntries() as ObservedPerfEntry[]) {
54
+ metrics.paints[entry.name] = entry.startTime
55
+ }
56
+ }).observe({ type: 'paint', buffered: true } as never)
57
+
58
+ new PerformanceObserver((list) => {
59
+ const entries = list.getEntries() as ObservedPerfEntry[]
60
+ const lastEntry = entries[entries.length - 1]
61
+ if (!lastEntry) {
62
+ return
63
+ }
64
+ metrics.lcp = lastEntry.startTime
65
+ }).observe({ type: 'largest-contentful-paint', buffered: true } as never)
66
+
67
+ new PerformanceObserver((list) => {
68
+ for (const entry of list.getEntries() as ObservedPerfEntry[]) {
69
+ if (entry.hadRecentInput) {
70
+ continue
71
+ }
72
+ metrics.cls += entry.value || 0
73
+ }
74
+ }).observe({ type: 'layout-shift', buffered: true } as never)
75
+ })
76
+
77
+ await page.reload({ waitUntil: 'domcontentloaded' })
78
+
79
+ const report = await page.evaluate(() => {
80
+ const perfGlobal = globalThis as typeof globalThis & {
81
+ __pwPerfMetrics?: PerfMetrics
82
+ }
83
+ const nav = performance.getEntriesByType('navigation' as never)[0] as unknown as
84
+ | NavigationTimingEntry
85
+ | undefined
86
+ const metrics = perfGlobal.__pwPerfMetrics
87
+
88
+ return {
89
+ ttfb: nav?.responseStart || 0,
90
+ domContentLoaded: nav?.domContentLoadedEventEnd || 0,
91
+ load: nav?.loadEventEnd || 0,
92
+ fcp: metrics?.paints['first-contentful-paint'] || 0,
93
+ lcp: metrics?.lcp || 0,
94
+ cls: metrics?.cls || 0,
95
+ }
96
+ })
97
+
98
+ console.log(report)
99
+ }
100
+
101
+ // Example: Measure the biggest transferred requests with raw CDP network events
102
+ async function collectHeaviestRequests() {
103
+ const cdp = await getCDPSession({ page })
104
+ await cdp.send('Network.enable')
105
+ await cdp.send('Network.setCacheDisabled', { cacheDisabled: true })
106
+
107
+ const responses = new Map<string, { url: string; mimeType: string }>()
108
+ const finished = new Map<string, number>()
109
+
110
+ cdp.on('Network.responseReceived', (event) => {
111
+ responses.set(event.requestId, {
112
+ url: event.response.url,
113
+ mimeType: event.response.mimeType,
114
+ })
115
+ })
116
+
117
+ cdp.on('Network.loadingFinished', (event) => {
118
+ finished.set(event.requestId, event.encodedDataLength)
119
+ })
120
+
121
+ await page.reload({ waitUntil: 'domcontentloaded' })
122
+
123
+ const largest = [...responses.entries()]
124
+ .map(([requestId, response]) => {
125
+ return {
126
+ url: response.url,
127
+ mimeType: response.mimeType,
128
+ bytes: finished.get(requestId) || 0,
129
+ }
130
+ })
131
+ .sort((a, b) => b.bytes - a.bytes)
132
+ .slice(0, 10)
133
+
134
+ console.log(largest)
135
+ }
136
+
137
+ // Example: Check whether interactivity is blocked by long tasks or slow events
138
+ async function measureInteractivity() {
139
+ await page.evaluate(() => {
140
+ const perfGlobal = globalThis as typeof globalThis & {
141
+ __pwLongTasks?: LongTaskEntry[]
142
+ __pwEventTimings?: EventTimingEntry[]
143
+ }
144
+
145
+ perfGlobal.__pwLongTasks = []
146
+ perfGlobal.__pwEventTimings = []
147
+
148
+ new PerformanceObserver((list) => {
149
+ perfGlobal.__pwLongTasks?.push(
150
+ ...(list.getEntries() as ObservedPerfEntry[]).map((entry) => ({
151
+ startTime: entry.startTime,
152
+ duration: entry.duration,
153
+ })),
154
+ )
155
+ }).observe({ type: 'longtask', buffered: true } as never)
156
+
157
+ new PerformanceObserver((list) => {
158
+ perfGlobal.__pwEventTimings?.push(
159
+ ...(list.getEntries() as ObservedPerfEntry[]).map((entry) => ({
160
+ name: entry.name,
161
+ duration: entry.duration,
162
+ interactionId: entry.interactionId || 0,
163
+ })),
164
+ )
165
+ }).observe({ type: 'event', buffered: true, durationThreshold: 16 } as never)
166
+ })
167
+
168
+ const button = page.getByRole('button').first()
169
+ await button.click()
170
+
171
+ const report = await page.evaluate(() => {
172
+ const perfGlobal = globalThis as typeof globalThis & {
173
+ __pwLongTasks?: LongTaskEntry[]
174
+ __pwEventTimings?: EventTimingEntry[]
175
+ }
176
+
177
+ return {
178
+ longTasks: (perfGlobal.__pwLongTasks || []).filter((entry) => entry.duration >= 50),
179
+ events: (perfGlobal.__pwEventTimings || []).filter((entry) => entry.interactionId !== 0),
180
+ }
181
+ })
182
+
183
+ console.log(report)
184
+ }
185
+
186
+ export { collectWebVitals, collectHeaviestRequests, measureInteractivity }
@@ -0,0 +1,59 @@
1
+ /**
2
+ * Conditional playwright-core import. When patchright mode is enabled via
3
+ * TABWRIGHT_PATCHRIGHT=1 env var, imports from @playwriter/patchright-core
4
+ * instead of @xmorse/playwright-core. Both packages expose identical APIs.
5
+ *
6
+ * Type imports continue to use @xmorse/playwright-core directly (types are
7
+ * the same in both packages) so TypeScript resolution works without the
8
+ * optional patchright dep being installed.
9
+ */
10
+
11
+ export type {
12
+ Page,
13
+ Frame,
14
+ Browser,
15
+ BrowserContext,
16
+ Locator,
17
+ FrameLocator,
18
+ ElementHandle,
19
+ CDPSession,
20
+ MouseActionEvent,
21
+ } from '@xmorse/playwright-core'
22
+
23
+ export type { BrowserType } from '@xmorse/playwright-core'
24
+
25
+ type Chromium = typeof import('@xmorse/playwright-core').chromium
26
+
27
+ let _chromium: Chromium | undefined
28
+
29
+ /**
30
+ * Returns the chromium BrowserType, loading from patchright-core if enabled.
31
+ * Caches after first call.
32
+ */
33
+ export async function getChromium(): Promise<Chromium> {
34
+ if (_chromium) {
35
+ return _chromium
36
+ }
37
+ if (isPatchrightEnabled()) {
38
+ try {
39
+ // Dynamic import — @playwriter/patchright-core is an optional dependency.
40
+ // Types come from @xmorse/playwright-core (identical API surface).
41
+ const mod: { chromium: Chromium } = await import('@playwriter/patchright-core' as string)
42
+ _chromium = mod.chromium
43
+ } catch (e: unknown) {
44
+ throw new Error(
45
+ '@playwriter/patchright-core is not installed. Install it with: pnpm add @playwriter/patchright-core',
46
+ { cause: e },
47
+ )
48
+ }
49
+ } else {
50
+ const mod = await import('@xmorse/playwright-core')
51
+ _chromium = mod.chromium
52
+ }
53
+ return _chromium!
54
+ }
55
+
56
+ export function isPatchrightEnabled(): boolean {
57
+ const value = process.env.TABWRIGHT_PATCHRIGHT || process.env.PLAYWRITER_PATCHRIGHT
58
+ return value === '1' || value === 'true'
59
+ }
@@ -0,0 +1,163 @@
1
+ /**
2
+ * Tests for auto-relocation of popup windows to tabs.
3
+ * - Connected source tab → popup gets relocated + auto-attached.
4
+ * - No source tab connected → popup is left alone.
5
+ */
6
+
7
+ import { createMCPClient } from './mcp-client.js'
8
+ import { describe, it, expect, beforeAll, afterAll } from 'vitest'
9
+ import {
10
+ setupTestContext,
11
+ cleanupTestContext,
12
+ getExtensionServiceWorker,
13
+ createSimpleServer,
14
+ type TestContext,
15
+ type SimpleServer,
16
+ js,
17
+ } from './test-utils.js'
18
+ import './test-declarations.js'
19
+
20
+ const TEST_PORT = 19995
21
+
22
+ describe('Popup window relocation', () => {
23
+ let client: Awaited<ReturnType<typeof createMCPClient>>['client']
24
+ let cleanup: (() => Promise<void>) | null = null
25
+ let testCtx: TestContext | null = null
26
+ let htmlServer: SimpleServer | null = null
27
+
28
+ beforeAll(async () => {
29
+ testCtx = await setupTestContext({
30
+ port: TEST_PORT,
31
+ tempDirPrefix: 'pw-popup-test-',
32
+ toggleExtension: true,
33
+ })
34
+
35
+ const result = await createMCPClient({ port: TEST_PORT })
36
+ client = result.client
37
+ cleanup = result.cleanup
38
+
39
+ htmlServer = await createSimpleServer({
40
+ routes: {
41
+ '/opener': `<!doctype html>
42
+ <html>
43
+ <body>
44
+ <button id="open-popup" onclick="window.open('/target', '', 'width=400,height=300,popup=1')">Open popup</button>
45
+ </body>
46
+ </html>`,
47
+ '/target': `<!doctype html>
48
+ <html>
49
+ <body>
50
+ <h1 id="target-heading">Popup target page</h1>
51
+ </body>
52
+ </html>`,
53
+ },
54
+ })
55
+ }, 600000)
56
+
57
+ afterAll(async () => {
58
+ if (htmlServer) {
59
+ await htmlServer.close()
60
+ }
61
+ await cleanupTestContext(testCtx, cleanup)
62
+ cleanup = null
63
+ testCtx = null
64
+ })
65
+
66
+ it('relocates popup window into main browser context as a tab', async () => {
67
+ if (!htmlServer) throw new Error('html server not initialized')
68
+ const browserContext = testCtx!.browserContext
69
+ const serviceWorker = await getExtensionServiceWorker(browserContext)
70
+
71
+ await client.callTool({
72
+ name: 'execute',
73
+ arguments: {
74
+ code: js`
75
+ state.popupTestPage = context.pages().find((p) => p.url() === 'about:blank') ?? (await context.newPage());
76
+ await state.popupTestPage.goto('${htmlServer.baseUrl}/opener', { waitUntil: 'domcontentloaded' });
77
+ return { url: state.popupTestPage.url(), pagesBefore: context.pages().length };
78
+ `,
79
+ },
80
+ })
81
+
82
+ const clickResult = await client.callTool({
83
+ name: 'execute',
84
+ arguments: {
85
+ code: js`
86
+ await state.popupTestPage.click('#open-popup');
87
+ await state.popupTestPage.waitForTimeout(1500);
88
+ return { pagesAfter: context.pages().length, allUrls: context.pages().map((p) => p.url()) };
89
+ `,
90
+ },
91
+ })
92
+
93
+ const clickOutput = (clickResult as any).content[0].text as string
94
+
95
+ expect(clickOutput).toContain('/target')
96
+ expect((clickResult as any).isError).not.toBe(true)
97
+ expect(clickOutput).toContain('[WARNING] New page opened from current page')
98
+ expect(clickOutput).not.toContain('Popup window detected')
99
+ expect(clickOutput).not.toContain('cannot be controlled by playwriter')
100
+
101
+ const windowTypes = await serviceWorker.evaluate(async () => {
102
+ const windows = await chrome.windows.getAll({ populate: false })
103
+ return windows.map((w) => w.type)
104
+ })
105
+ expect(windowTypes).not.toContain('popup')
106
+ expect(windowTypes).toContain('normal')
107
+
108
+ await client.callTool({
109
+ name: 'execute',
110
+ arguments: {
111
+ code: js`
112
+ const targetPage = context.pages().find((p) => p.url().endsWith('/target'));
113
+ if (targetPage) { await targetPage.close(); }
114
+ await state.popupTestPage.close();
115
+ delete state.popupTestPage;
116
+ `,
117
+ },
118
+ })
119
+ }, 60000)
120
+
121
+ it('leaves popup windows alone when no Tabwright tab is connected', async () => {
122
+ if (!htmlServer) throw new Error('html server not initialized')
123
+ const browserContext = testCtx!.browserContext
124
+ const serviceWorker = await getExtensionServiceWorker(browserContext)
125
+
126
+ await serviceWorker.evaluate(async () => {
127
+ await globalThis.disconnectEverything()
128
+ })
129
+ await new Promise((r) => {
130
+ setTimeout(r, 200)
131
+ })
132
+
133
+ // Drive the browser directly via browserContext (not MCP/extension) so
134
+ // the opener tab stays unconnected.
135
+ const unconnectedPage = await browserContext.newPage()
136
+ await unconnectedPage.goto(`${htmlServer.baseUrl}/opener`, { waitUntil: 'domcontentloaded' })
137
+ await unconnectedPage.bringToFront()
138
+ await unconnectedPage.click('#open-popup')
139
+ // If relocation DID happen, the popup window would be gone by now.
140
+ await new Promise((r) => {
141
+ setTimeout(r, 1500)
142
+ })
143
+
144
+ const windowTypes = await serviceWorker.evaluate(async () => {
145
+ const windows = await chrome.windows.getAll({ populate: false })
146
+ return windows.map((w) => w.type)
147
+ })
148
+ expect(windowTypes).toContain('popup')
149
+
150
+ const windowIdsToClose = await serviceWorker.evaluate(async () => {
151
+ const windows = await chrome.windows.getAll({ populate: false })
152
+ return windows.filter((w) => w.type === 'popup').map((w) => w.id)
153
+ })
154
+ for (const windowId of windowIdsToClose) {
155
+ if (windowId !== undefined) {
156
+ await serviceWorker.evaluate(async (id: number) => {
157
+ await chrome.windows.remove(id)
158
+ }, windowId)
159
+ }
160
+ }
161
+ await unconnectedPage.close()
162
+ }, 60000)
163
+ })