playwriter 0.0.105 → 0.2.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 (79) hide show
  1. package/dist/bippy.js +5 -5
  2. package/dist/cdp-relay.d.ts.map +1 -1
  3. package/dist/cdp-relay.js +17 -5
  4. package/dist/cdp-relay.js.map +1 -1
  5. package/dist/cli-help.test.d.ts +2 -0
  6. package/dist/cli-help.test.d.ts.map +1 -0
  7. package/dist/cli-help.test.js +53 -0
  8. package/dist/cli-help.test.js.map +1 -0
  9. package/dist/cli.js +74 -25
  10. package/dist/cli.js.map +1 -1
  11. package/dist/executor.d.ts +1 -0
  12. package/dist/executor.d.ts.map +1 -1
  13. package/dist/executor.js +55 -12
  14. package/dist/executor.js.map +1 -1
  15. package/dist/extension/background.js +675 -27
  16. package/dist/extension/manifest.json +1 -1
  17. package/dist/ghost-cursor-client.js +170 -83
  18. package/dist/{recording-ghost-cursor.d.ts → ghost-cursor-controller.d.ts} +15 -10
  19. package/dist/ghost-cursor-controller.d.ts.map +1 -0
  20. package/dist/ghost-cursor-controller.js +98 -0
  21. package/dist/ghost-cursor-controller.js.map +1 -0
  22. package/dist/ghost-cursor.d.ts.map +1 -1
  23. package/dist/ghost-cursor.js +42 -26
  24. package/dist/ghost-cursor.js.map +1 -1
  25. package/dist/mcp.d.ts.map +1 -1
  26. package/dist/mcp.js +6 -1
  27. package/dist/mcp.js.map +1 -1
  28. package/dist/on-mouse-action.test.js +25 -0
  29. package/dist/on-mouse-action.test.js.map +1 -1
  30. package/dist/performance-examples.d.ts +5 -0
  31. package/dist/performance-examples.d.ts.map +1 -0
  32. package/dist/performance-examples.js +112 -0
  33. package/dist/performance-examples.js.map +1 -0
  34. package/dist/performance-profiling.md +417 -0
  35. package/dist/prompt.md +22 -8
  36. package/dist/react-source.d.ts +44 -0
  37. package/dist/react-source.d.ts.map +1 -1
  38. package/dist/react-source.js +207 -20
  39. package/dist/react-source.js.map +1 -1
  40. package/dist/readability.js +1 -1
  41. package/dist/relay-core.test.d.ts.map +1 -1
  42. package/dist/relay-core.test.js +101 -1
  43. package/dist/relay-core.test.js.map +1 -1
  44. package/dist/relay-session.test.js +34 -6
  45. package/dist/relay-session.test.js.map +1 -1
  46. package/dist/screen-recording.d.ts +2 -2
  47. package/dist/screen-recording.d.ts.map +1 -1
  48. package/dist/screen-recording.js +19 -7
  49. package/dist/screen-recording.js.map +1 -1
  50. package/dist/selector-generator.js +1 -1
  51. package/package.json +7 -7
  52. package/src/aria-snapshots/github-interactive.txt +5 -3
  53. package/src/aria-snapshots/github-raw.txt +8 -5
  54. package/src/aria-snapshots/hackernews-interactive.txt +241 -238
  55. package/src/aria-snapshots/hackernews-raw.txt +269 -265
  56. package/src/aria-snapshots/prosemirror-interactive.txt +3 -1
  57. package/src/aria-snapshots/prosemirror-raw.txt +4 -1
  58. package/src/assets/aria-labels-hacker-news.png +0 -0
  59. package/src/assets/aria-labels-old-reddit.png +0 -0
  60. package/src/cdp-relay.ts +17 -5
  61. package/src/cli-help.test.ts +63 -0
  62. package/src/cli.ts +80 -28
  63. package/src/executor.ts +65 -15
  64. package/src/ghost-cursor-client.ts +221 -96
  65. package/src/{recording-ghost-cursor.ts → ghost-cursor-controller.ts} +50 -34
  66. package/src/ghost-cursor.ts +54 -41
  67. package/src/mcp.ts +6 -1
  68. package/src/on-mouse-action.test.ts +30 -0
  69. package/src/performance-examples.ts +186 -0
  70. package/src/react-source.ts +310 -24
  71. package/src/relay-core.test.ts +117 -0
  72. package/src/relay-session.test.ts +36 -10
  73. package/src/screen-recording.ts +23 -10
  74. package/src/skill.md +33 -9
  75. package/src/snapshots/shadcn-ui-accessibility-full.md +6 -3
  76. package/src/snapshots/shadcn-ui-accessibility-interactive.md +2 -0
  77. package/dist/recording-ghost-cursor.d.ts.map +0 -1
  78. package/dist/recording-ghost-cursor.js +0 -79
  79. package/dist/recording-ghost-cursor.js.map +0 -1
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "manifest_version": 3,
3
3
  "name": "Playwriter",
4
- "version": "0.0.81",
4
+ "version": "0.0.94",
5
5
  "description": "Automate your Browser using Cursor, Claude, VS Code. More capable and context efficient than Playwright MCP.",
6
6
  "permissions": [
7
7
  "debugger",
@@ -31,31 +31,46 @@
31
31
  var SCREENSTUDIO_POINTER_MACOS_TAHOE_DATA_URL = "data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iNjE4IiBoZWlnaHQ9Ijk1OCIgdmlld0JveD0iMCAwIDYxOCA5NTgiIGZpbGw9Im5vbmUiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyI+CjxnIGZpbHRlcj0idXJsKCNmaWx0ZXIwX2RfMzg0XzI3KSI+CjxwYXRoIGZpbGwtcnVsZT0iZXZlbm9kZCIgY2xpcC1ydWxlPSJldmVub2RkIiBkPSJNMTI3LjA2MiAzNy4wMzMxTDU0MC42OTYgNDUxLjU1NUM1OTIuNjUzIDUwMy42NiA1NTUuNzk0IDU5Mi41NzQgNDgyLjIyNiA1OTIuNTc0TDQyMS44MzEgNTkyLjU2OUw0ODEuODIxIDczNS4wNTRDNDkyLjMzMSA3NjAuMDIxIDQ5Mi40NzkgNzg3LjY1MiA0ODIuMjY1IDgxMi43NjdDNDcyLjAwMiA4MzcuOTMyIDQ1Mi41NjEgODU3LjU3IDQyNy40OTYgODY4LjA4QzQxNC44NjQgODczLjM1OSA0MDEuNjQgODc2LjAyNCAzODguMTIxIDg3Ni4wMjRDMzQ3LjExNyA4NzYuMDI0IDMxMC4zNTggODUxLjYgMjk0LjQ3IDgxMy44MDRMMjMxLjQyIDY2My45MThMMTkwLjM2OCA3MDAuMzM3QzEzNy4wMjkgNzQ3LjUwOCA1MyA3MDkuNjYzIDUzIDYzOC40MTNWNjcuNjc0NEM1MyAyOC45OTAzIDk5LjcyNjggOS42NDgyOCAxMjcuMDYyIDM3LjAzMzFaIiBmaWxsPSJ3aGl0ZSIvPgo8cGF0aCBmaWxsLXJ1bGU9ImV2ZW5vZGQiIGNsaXAtcnVsZT0iZXZlbm9kZCIgZD0iTTEwMi4zMTYgOTkuNjUyQzEwMi4zMTYgOTMuMTg4MiAxMTAuMTYyIDg5LjkzMTYgMTE0LjcwMSA5NC41MjA0TDUwNC44OTcgNDg1LjU1NUM1MjYuMTY0IDUwNi44NzEgNTExLjA2NSA1NDMuMjM2IDQ4MC45NjcgNTQzLjIzNkwzNDcuNTQ2IDU0My4xNjFMNDM2LjM0MiA3NTQuMTQzQzQ0Ny41NDIgNzgwLjc4OCA0MzUuMDA5IDgxMS40MjkgNDA4LjQxNCA4MjIuNTgxQzM4MS43MiA4MzMuNzgxIDM1MS4xMjggODIxLjI5OCAzMzkuOTc3IDc5NC43MDJMMjUwLjI5MyA1ODEuMzUyTDE1OC41MTcgNjYyLjY0NEMxMzcuOTkxIDY4MC44MDEgMTA2LjMxOSA2NjguMTQ1IDEwMi42NjQgNjQyLjMyM0wxMDIuMzE2IDYzNy4zMzFWOTkuNjUyWiIgZmlsbD0iYmxhY2siLz4KPC9nPgo8ZGVmcz4KPGZpbHRlciBpZD0iZmlsdGVyMF9kXzM4NF8yNyIgeD0iMC4zNCIgeT0iMC43OTkyMTkiIHdpZHRoPSI2MTcuMzIiIGhlaWdodD0iOTU3LjE0NCIgZmlsdGVyVW5pdHM9InVzZXJTcGFjZU9uVXNlIiBjb2xvci1pbnRlcnBvbGF0aW9uLWZpbHRlcnM9InNSR0IiPgo8ZmVGbG9vZCBmbG9vZC1vcGFjaXR5PSIwIiByZXN1bHQ9IkJhY2tncm91bmRJbWFnZUZpeCIvPgo8ZmVDb2xvck1hdHJpeCBpbj0iU291cmNlQWxwaGEiIHR5cGU9Im1hdHJpeCIgdmFsdWVzPSIwIDAgMCAwIDAgMCAwIDAgMCAwIDAgMCAwIDAgMCAwIDAgMCAxMjcgMCIgcmVzdWx0PSJoYXJkQWxwaGEiLz4KPGZlT2Zmc2V0IGR5PSIyOS4yNiIvPgo8ZmVHYXVzc2lhbkJsdXIgc3RkRGV2aWF0aW9uPSIyNi4zMyIvPgo8ZmVDb21wb3NpdGUgaW4yPSJoYXJkQWxwaGEiIG9wZXJhdG9yPSJvdXQiLz4KPGZlQ29sb3JNYXRyaXggdHlwZT0ibWF0cml4IiB2YWx1ZXM9IjAgMCAwIDAgMCAwIDAgMCAwIDAgMCAwIDAgMCAwIDAgMCAwIDAuNjUgMCIvPgo8ZmVCbGVuZCBtb2RlPSJub3JtYWwiIGluMj0iQmFja2dyb3VuZEltYWdlRml4IiByZXN1bHQ9ImVmZmVjdDFfZHJvcFNoYWRvd18zODRfMjciLz4KPGZlQmxlbmQgbW9kZT0ibm9ybWFsIiBpbj0iU291cmNlR3JhcGhpYyIgaW4yPSJlZmZlY3QxX2Ryb3BTaGFkb3dfMzg0XzI3IiByZXN1bHQ9InNoYXBlIi8+CjwvZmlsdGVyPgo8L2RlZnM+Cjwvc3ZnPg==";
32
32
 
33
33
  // src/ghost-cursor-client.ts
34
+ var isTopFrame = (() => {
35
+ try {
36
+ return window === window.top;
37
+ } catch {
38
+ return false;
39
+ }
40
+ })();
34
41
  var CURSOR_ID = "__playwriter_ghost_cursor__";
35
42
  var SCREENSTUDIO_POINTER_ASPECT_RATIO = 618 / 958;
36
43
  var SCREENSTUDIO_HOTSPOT_X_RATIO = 0.14;
37
44
  var SCREENSTUDIO_HOTSPOT_Y_RATIO = 0.06;
38
45
  var MINIMAL_TRIANGLE_HOTSPOT_X_RATIO = 0.07;
39
46
  var MINIMAL_TRIANGLE_HOTSPOT_Y_RATIO = 0.06;
47
+ var MOVE_EASING = "cubic-bezier(0.65, 0, 0.35, 1)";
48
+ var PRESS_EASING = "cubic-bezier(0.23, 1, 0.32, 1)";
49
+ var PRESS_DURATION_MS = 140;
50
+ var IDLE_HIDE_DELAY_MS = 5000;
51
+ var IDLE_FADE_OUT_MS = 600;
40
52
  var DEFAULT_OPTIONS = {
41
53
  style: "minimal",
42
54
  color: "#111827",
43
55
  size: 22,
44
56
  zIndex: 2147483647,
45
- easing: "cubic-bezier(0.16, 1, 0.3, 1)",
46
- minDurationMs: 24,
47
- maxDurationMs: 320,
48
- speedPxPerMs: 4
57
+ easing: MOVE_EASING,
58
+ minDurationMs: 220,
59
+ maxDurationMs: 1500,
60
+ speedPxPerMs: 1.2
49
61
  };
50
62
  var runtime = {
51
- cursorElement: null,
63
+ outerElement: null,
64
+ innerElement: null,
52
65
  options: DEFAULT_OPTIONS,
53
66
  x: 0,
54
67
  y: 0,
55
68
  scale: 1,
56
69
  hasPosition: false,
57
- enabled: false
70
+ enabled: false,
71
+ idleHidden: false
58
72
  };
73
+ var idleHideTimer = null;
59
74
  function clamp(options) {
60
75
  const { value, min, max } = options;
61
76
  return Math.min(max, Math.max(min, value));
@@ -115,12 +130,18 @@
115
130
  }
116
131
  return "0.72";
117
132
  }
118
- function applyTransform() {
119
- if (!runtime.cursorElement) {
133
+ function applyTranslate() {
134
+ if (!runtime.outerElement) {
120
135
  return;
121
136
  }
122
137
  const hotspot = getHotspotOffsetPx();
123
- runtime.cursorElement.style.transform = `translate3d(${runtime.x - hotspot.x}px, ${runtime.y - hotspot.y}px, 0) scale(${runtime.scale})`;
138
+ runtime.outerElement.style.transform = `translate3d(${runtime.x - hotspot.x}px, ${runtime.y - hotspot.y}px, 0)`;
139
+ }
140
+ function applyScale() {
141
+ if (!runtime.innerElement) {
142
+ return;
143
+ }
144
+ runtime.innerElement.style.transform = `scale(${runtime.scale})`;
124
145
  }
125
146
  function computeDurationMs(options) {
126
147
  if (!runtime.hasPosition) {
@@ -137,103 +158,146 @@
137
158
  });
138
159
  }
139
160
  function createCursorElement() {
140
- const element = document.createElement("div");
141
- element.id = CURSOR_ID;
142
- element.setAttribute("aria-hidden", "true");
143
- element.style.position = "fixed";
144
- element.style.left = "0";
145
- element.style.top = "0";
146
- element.style.pointerEvents = "none";
147
- element.style.zIndex = `${runtime.options.zIndex}`;
148
- element.style.opacity = getBaseOpacity();
149
- element.style.transitionProperty = "transform, opacity";
150
- element.style.transitionTimingFunction = runtime.options.easing;
151
- element.style.transitionDuration = "0ms";
152
- element.style.willChange = "transform";
153
- runtime.cursorElement = element;
161
+ const outer = document.createElement("div");
162
+ outer.id = CURSOR_ID;
163
+ outer.setAttribute("aria-hidden", "true");
164
+ outer.style.position = "fixed";
165
+ outer.style.left = "0";
166
+ outer.style.top = "0";
167
+ outer.style.pointerEvents = "none";
168
+ outer.style.zIndex = `${runtime.options.zIndex}`;
169
+ outer.style.transitionProperty = "transform";
170
+ outer.style.transitionTimingFunction = runtime.options.easing;
171
+ outer.style.transitionDuration = "0ms";
172
+ outer.style.willChange = "transform";
173
+ const inner = document.createElement("div");
174
+ inner.style.transitionProperty = "transform, opacity";
175
+ inner.style.transitionTimingFunction = PRESS_EASING;
176
+ inner.style.transitionDuration = `${PRESS_DURATION_MS}ms`;
177
+ inner.style.opacity = getBaseOpacity();
178
+ outer.appendChild(inner);
179
+ runtime.outerElement = outer;
180
+ runtime.innerElement = inner;
154
181
  applyRuntimeVisualOptions();
155
- return element;
182
+ return outer;
156
183
  }
157
184
  function ensureCursorElement() {
158
185
  const existing = document.getElementById(CURSOR_ID);
159
186
  if (existing) {
160
- runtime.cursorElement = existing;
187
+ runtime.outerElement = existing;
188
+ runtime.innerElement = existing.firstElementChild || null;
161
189
  return existing;
162
190
  }
163
- const element = createCursorElement();
164
- runtime.cursorElement = element;
191
+ const outer = createCursorElement();
165
192
  const root = document.documentElement || document.body;
166
- root.appendChild(element);
167
- return element;
193
+ root.appendChild(outer);
194
+ return outer;
168
195
  }
169
196
  function applyRuntimeVisualOptions() {
170
- if (!runtime.cursorElement) {
197
+ if (!runtime.innerElement) {
171
198
  return;
172
199
  }
173
200
  const dimensions = getCursorDimensions();
174
- runtime.cursorElement.style.width = `${dimensions.width}px`;
175
- runtime.cursorElement.style.height = `${dimensions.height}px`;
176
- runtime.cursorElement.style.zIndex = `${runtime.options.zIndex}`;
177
- runtime.cursorElement.style.transitionTimingFunction = runtime.options.easing;
201
+ runtime.innerElement.style.width = `${dimensions.width}px`;
202
+ runtime.innerElement.style.height = `${dimensions.height}px`;
203
+ if (runtime.outerElement) {
204
+ runtime.outerElement.style.zIndex = `${runtime.options.zIndex}`;
205
+ runtime.outerElement.style.transitionTimingFunction = runtime.options.easing;
206
+ }
207
+ const hotspot = getHotspotOffsetPx();
208
+ runtime.innerElement.style.transformOrigin = `${hotspot.x}px ${hotspot.y}px`;
178
209
  if (runtime.options.style === "screenstudio") {
179
- runtime.cursorElement.style.borderRadius = "0";
180
- runtime.cursorElement.style.border = "none";
181
- runtime.cursorElement.style.backgroundColor = "transparent";
182
- runtime.cursorElement.style.backgroundImage = `url("${SCREENSTUDIO_POINTER_MACOS_TAHOE_DATA_URL}")`;
183
- runtime.cursorElement.style.backgroundRepeat = "no-repeat";
184
- runtime.cursorElement.style.backgroundPosition = "left top";
185
- runtime.cursorElement.style.backgroundSize = "contain";
186
- runtime.cursorElement.style.backdropFilter = "none";
187
- runtime.cursorElement.style.filter = "none";
188
- runtime.cursorElement.style.boxShadow = "none";
189
- runtime.cursorElement.style.opacity = getBaseOpacity();
210
+ runtime.innerElement.style.borderRadius = "0";
211
+ runtime.innerElement.style.border = "none";
212
+ runtime.innerElement.style.backgroundColor = "transparent";
213
+ runtime.innerElement.style.backgroundImage = `url("${SCREENSTUDIO_POINTER_MACOS_TAHOE_DATA_URL}")`;
214
+ runtime.innerElement.style.backgroundRepeat = "no-repeat";
215
+ runtime.innerElement.style.backgroundPosition = "left top";
216
+ runtime.innerElement.style.backgroundSize = "contain";
217
+ runtime.innerElement.style.backdropFilter = "none";
218
+ runtime.innerElement.style.filter = "none";
219
+ runtime.innerElement.style.boxShadow = "none";
220
+ runtime.innerElement.style.opacity = getBaseOpacity();
190
221
  return;
191
222
  }
192
223
  if (runtime.options.style === "minimal") {
193
224
  const triangleSvg = `<svg xmlns="http://www.w3.org/2000/svg" width="32" height="32" viewBox="-1 -1 26 26"><path fill="white" stroke="${runtime.options.color}" stroke-width="1.5" stroke-linejoin="round" d="m23.284 19.124l-6.866-6.895a.4.4 0 0 1-.118-.296a.43.43 0 0 1 .163-.282l4.439-3.077a1.48 1.48 0 0 0 .621-1.48a1.48 1.48 0 0 0-1.036-1.198L1.623.302a1.14 1.14 0 0 0-1.11.282A1.13 1.13 0 0 0 .29 1.649L5.928 20.44a1.48 1.48 0 0 0 1.183 1.035a1.48 1.48 0 0 0 1.48-.621l3.078-4.44a.37.37 0 0 1 .31-.118a.43.43 0 0 1 .296.104l6.91 6.91a1.48 1.48 0 0 0 2.087 0l2.086-2.086a1.48 1.48 0 0 0-.074-2.101"/></svg>`;
194
225
  const triangleDataUrl = `url("data:image/svg+xml,${encodeURIComponent(triangleSvg)}")`;
195
- runtime.cursorElement.style.borderRadius = "0";
196
- runtime.cursorElement.style.border = "none";
197
- runtime.cursorElement.style.backgroundColor = "transparent";
198
- runtime.cursorElement.style.backgroundImage = triangleDataUrl;
199
- runtime.cursorElement.style.backgroundRepeat = "no-repeat";
200
- runtime.cursorElement.style.backgroundSize = "contain";
201
- runtime.cursorElement.style.backgroundPosition = "left top";
202
- runtime.cursorElement.style.backdropFilter = "none";
203
- runtime.cursorElement.style.boxShadow = "none";
204
- runtime.cursorElement.style.filter = "drop-shadow(0 1px 2px rgba(0, 0, 0, 0.4))";
205
- runtime.cursorElement.style.opacity = getBaseOpacity();
226
+ runtime.innerElement.style.borderRadius = "0";
227
+ runtime.innerElement.style.border = "none";
228
+ runtime.innerElement.style.backgroundColor = "transparent";
229
+ runtime.innerElement.style.backgroundImage = triangleDataUrl;
230
+ runtime.innerElement.style.backgroundRepeat = "no-repeat";
231
+ runtime.innerElement.style.backgroundSize = "contain";
232
+ runtime.innerElement.style.backgroundPosition = "left top";
233
+ runtime.innerElement.style.backdropFilter = "none";
234
+ runtime.innerElement.style.boxShadow = "none";
235
+ runtime.innerElement.style.filter = "drop-shadow(0 1px 2px rgba(0, 0, 0, 0.4))";
236
+ runtime.innerElement.style.opacity = getBaseOpacity();
206
237
  return;
207
238
  }
208
- runtime.cursorElement.style.borderRadius = "999px";
209
- runtime.cursorElement.style.border = "none";
210
- runtime.cursorElement.style.backgroundColor = runtime.options.color;
211
- runtime.cursorElement.style.backgroundImage = "none";
212
- runtime.cursorElement.style.backdropFilter = "none";
213
- runtime.cursorElement.style.filter = "none";
214
- runtime.cursorElement.style.boxShadow = "0 2px 10px rgba(0, 0, 0, 0.18), inset 0 0 0 2px rgba(255, 255, 255, 0.55)";
215
- runtime.cursorElement.style.opacity = getBaseOpacity();
239
+ runtime.innerElement.style.borderRadius = "999px";
240
+ runtime.innerElement.style.border = "none";
241
+ runtime.innerElement.style.backgroundColor = runtime.options.color;
242
+ runtime.innerElement.style.backgroundImage = "none";
243
+ runtime.innerElement.style.backdropFilter = "none";
244
+ runtime.innerElement.style.filter = "none";
245
+ runtime.innerElement.style.boxShadow = "0 2px 10px rgba(0, 0, 0, 0.18), inset 0 0 0 2px rgba(255, 255, 255, 0.55)";
246
+ runtime.innerElement.style.opacity = getBaseOpacity();
247
+ }
248
+ function clearIdleHideTimer() {
249
+ if (idleHideTimer !== null) {
250
+ clearTimeout(idleHideTimer);
251
+ idleHideTimer = null;
252
+ }
253
+ }
254
+ function scheduleIdleHide() {
255
+ clearIdleHideTimer();
256
+ idleHideTimer = setTimeout(() => {
257
+ idleHideTimer = null;
258
+ if (!runtime.enabled || !runtime.innerElement) {
259
+ return;
260
+ }
261
+ runtime.idleHidden = true;
262
+ runtime.innerElement.style.transitionDuration = `${IDLE_FADE_OUT_MS}ms`;
263
+ runtime.innerElement.style.transitionTimingFunction = PRESS_EASING;
264
+ runtime.innerElement.style.opacity = "0";
265
+ }, IDLE_HIDE_DELAY_MS);
266
+ }
267
+ function wakeFromIdle(options) {
268
+ runtime.x = options.x;
269
+ runtime.y = options.y;
270
+ runtime.hasPosition = true;
271
+ if (runtime.innerElement) {
272
+ runtime.innerElement.style.transitionDuration = `${PRESS_DURATION_MS}ms`;
273
+ runtime.innerElement.style.transitionTimingFunction = PRESS_EASING;
274
+ runtime.innerElement.style.opacity = getBaseOpacity();
275
+ }
216
276
  }
217
277
  function moveCursor(options) {
218
278
  if (!runtime.enabled) {
219
279
  return;
220
280
  }
221
- const element = ensureCursorElement();
281
+ ensureCursorElement();
222
282
  const durationMs = computeDurationMs({ targetX: options.x, targetY: options.y });
223
- element.style.transitionDuration = `${Math.round(durationMs)}ms`;
283
+ if (runtime.outerElement) {
284
+ runtime.outerElement.style.transitionDuration = `${Math.round(durationMs)}ms`;
285
+ runtime.outerElement.style.transitionTimingFunction = runtime.options.easing;
286
+ }
224
287
  runtime.x = options.x;
225
288
  runtime.y = options.y;
226
289
  runtime.hasPosition = true;
227
- applyTransform();
290
+ applyTranslate();
228
291
  }
229
292
  function setPressed(options) {
230
- if (!runtime.enabled) {
293
+ if (!runtime.enabled || !runtime.innerElement) {
231
294
  return;
232
295
  }
233
- const element = ensureCursorElement();
234
- runtime.scale = options.pressed ? runtime.options.style === "screenstudio" ? 0.94 : runtime.options.style === "minimal" ? 0.93 : 0.82 : 1;
235
- element.style.opacity = options.pressed ? "1" : getBaseOpacity();
236
- applyTransform();
296
+ runtime.scale = options.pressed ? runtime.options.style === "dot" ? 0.92 : 0.95 : 1;
297
+ runtime.innerElement.style.transitionDuration = `${PRESS_DURATION_MS}ms`;
298
+ runtime.innerElement.style.transitionTimingFunction = PRESS_EASING;
299
+ runtime.innerElement.style.opacity = options.pressed ? "1" : getBaseOpacity();
300
+ applyScale();
237
301
  }
238
302
  function enable(options) {
239
303
  runtime.options = mergeOptions(options);
@@ -246,34 +310,44 @@
246
310
  runtime.scale = 1;
247
311
  runtime.hasPosition = true;
248
312
  }
249
- applyTransform();
313
+ runtime.idleHidden = false;
314
+ if (runtime.innerElement) {
315
+ runtime.innerElement.style.opacity = getBaseOpacity();
316
+ }
317
+ applyTranslate();
318
+ applyScale();
319
+ scheduleIdleHide();
250
320
  }
251
321
  function disable() {
252
322
  runtime.enabled = false;
253
323
  runtime.scale = 1;
254
324
  runtime.hasPosition = false;
255
- if (runtime.cursorElement) {
256
- runtime.cursorElement.remove();
257
- runtime.cursorElement = null;
325
+ runtime.idleHidden = false;
326
+ clearIdleHideTimer();
327
+ if (runtime.outerElement) {
328
+ runtime.outerElement.remove();
329
+ runtime.outerElement = null;
330
+ runtime.innerElement = null;
258
331
  }
259
332
  }
260
333
  function applyMouseAction(action) {
261
334
  if (!runtime.enabled) {
262
335
  return;
263
336
  }
337
+ if (runtime.idleHidden) {
338
+ runtime.idleHidden = false;
339
+ wakeFromIdle({ x: action.x, y: action.y });
340
+ }
264
341
  if (action.type === "move" || action.type === "wheel") {
265
342
  moveCursor({ x: action.x, y: action.y });
266
- return;
267
- }
268
- if (action.type === "down") {
343
+ } else if (action.type === "down") {
269
344
  moveCursor({ x: action.x, y: action.y });
270
345
  setPressed({ pressed: true });
271
- return;
272
- }
273
- if (action.type === "up") {
346
+ } else if (action.type === "up") {
274
347
  moveCursor({ x: action.x, y: action.y });
275
348
  setPressed({ pressed: false });
276
349
  }
350
+ scheduleIdleHide();
277
351
  }
278
352
  var api = {
279
353
  enable,
@@ -283,5 +357,18 @@
283
357
  return runtime.enabled;
284
358
  }
285
359
  };
286
- globalThis.__playwriterGhostCursor = api;
360
+ if (isTopFrame) {
361
+ globalThis.__playwriterGhostCursor = api;
362
+ try {
363
+ if (document.readyState === "loading") {
364
+ document.addEventListener("DOMContentLoaded", () => {
365
+ try {
366
+ api.enable();
367
+ } catch {}
368
+ }, { once: true });
369
+ } else {
370
+ api.enable();
371
+ }
372
+ } catch {}
373
+ }
287
374
  })();
@@ -1,34 +1,39 @@
1
1
  /**
2
- * Encapsulates ghost cursor lifecycle for recording sessions.
3
- * Keeps onMouseAction chaining/restoration isolated from executor logic.
2
+ * Always-on ghost cursor controller (Node side).
3
+ *
4
+ * Wires page.onMouseAction → applyGhostCursorMouseAction for every page.
5
+ * Chains with any pre-existing onMouseAction callback. Cursor-apply is
6
+ * fire-and-forget via a per-page queue so it does not block action completion.
4
7
  */
5
8
  import type { BrowserContext, Page } from '@xmorse/playwright-core';
6
9
  import { type GhostCursorClientOptions } from './ghost-cursor.js';
7
- interface RecordingGhostCursorLogger {
10
+ interface GhostCursorLogger {
8
11
  error: (...args: unknown[]) => void;
9
12
  }
10
13
  interface RecordingTargetOptions {
11
14
  page?: Page;
12
15
  sessionId?: string;
13
16
  }
14
- export declare class RecordingGhostCursorController {
17
+ export declare class GhostCursorController {
15
18
  private readonly previousMouseActionByPage;
16
19
  private readonly cursorApplyQueueByPage;
20
+ private readonly attachedPages;
17
21
  private readonly logger;
18
22
  constructor(options: {
19
- logger: RecordingGhostCursorLogger;
23
+ logger: GhostCursorLogger;
20
24
  });
21
25
  resolveRecordingTargetPage(options: {
22
26
  context: BrowserContext;
23
27
  defaultPage: Page;
24
28
  target?: RecordingTargetOptions;
25
29
  }): Page;
26
- enableForRecording(options: {
30
+ /** Wire onMouseAction. Idempotent. */
31
+ attachToPage(options: {
27
32
  page: Page;
28
- }): Promise<void>;
29
- disableForRecording(options: {
33
+ }): void;
34
+ detachFromPage(options: {
30
35
  page: Page;
31
- }): Promise<void>;
36
+ }): void;
32
37
  show(options: {
33
38
  page: Page;
34
39
  cursorOptions?: GhostCursorClientOptions;
@@ -38,4 +43,4 @@ export declare class RecordingGhostCursorController {
38
43
  }): Promise<void>;
39
44
  }
40
45
  export {};
41
- //# sourceMappingURL=recording-ghost-cursor.d.ts.map
46
+ //# sourceMappingURL=ghost-cursor-controller.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ghost-cursor-controller.d.ts","sourceRoot":"","sources":["../src/ghost-cursor-controller.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,KAAK,EAAE,cAAc,EAAE,IAAI,EAAE,MAAM,yBAAyB,CAAA;AACnE,OAAO,EAIL,KAAK,wBAAwB,EAC9B,MAAM,mBAAmB,CAAA;AAE1B,UAAU,iBAAiB;IACzB,KAAK,EAAE,CAAC,GAAG,IAAI,EAAE,OAAO,EAAE,KAAK,IAAI,CAAA;CACpC;AAED,UAAU,sBAAsB;IAC9B,IAAI,CAAC,EAAE,IAAI,CAAA;IACX,SAAS,CAAC,EAAE,MAAM,CAAA;CACnB;AAED,qBAAa,qBAAqB;IAChC,OAAO,CAAC,QAAQ,CAAC,yBAAyB,CAA6C;IACvF,OAAO,CAAC,QAAQ,CAAC,sBAAsB,CAAqC;IAC5E,OAAO,CAAC,QAAQ,CAAC,aAAa,CAAsB;IACpD,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAmB;gBAE9B,OAAO,EAAE;QAAE,MAAM,EAAE,iBAAiB,CAAA;KAAE;IAIlD,0BAA0B,CAAC,OAAO,EAAE;QAClC,OAAO,EAAE,cAAc,CAAA;QACvB,WAAW,EAAE,IAAI,CAAA;QACjB,MAAM,CAAC,EAAE,sBAAsB,CAAA;KAChC,GAAG,IAAI;IAoBR,sCAAsC;IACtC,YAAY,CAAC,OAAO,EAAE;QAAE,IAAI,EAAE,IAAI,CAAA;KAAE,GAAG,IAAI;IAwC3C,cAAc,CAAC,OAAO,EAAE;QAAE,IAAI,EAAE,IAAI,CAAA;KAAE,GAAG,IAAI;IAWvC,IAAI,CAAC,OAAO,EAAE;QAAE,IAAI,EAAE,IAAI,CAAC;QAAC,aAAa,CAAC,EAAE,wBAAwB,CAAA;KAAE,GAAG,OAAO,CAAC,IAAI,CAAC;IAStF,IAAI,CAAC,OAAO,EAAE;QAAE,IAAI,EAAE,IAAI,CAAA;KAAE,GAAG,OAAO,CAAC,IAAI,CAAC;CAQnD"}
@@ -0,0 +1,98 @@
1
+ /**
2
+ * Always-on ghost cursor controller (Node side).
3
+ *
4
+ * Wires page.onMouseAction → applyGhostCursorMouseAction for every page.
5
+ * Chains with any pre-existing onMouseAction callback. Cursor-apply is
6
+ * fire-and-forget via a per-page queue so it does not block action completion.
7
+ */
8
+ import { applyGhostCursorMouseAction, disableGhostCursor, enableGhostCursor, } from './ghost-cursor.js';
9
+ export class GhostCursorController {
10
+ previousMouseActionByPage = new WeakMap();
11
+ cursorApplyQueueByPage = new WeakMap();
12
+ attachedPages = new WeakSet();
13
+ logger;
14
+ constructor(options) {
15
+ this.logger = options.logger;
16
+ }
17
+ resolveRecordingTargetPage(options) {
18
+ const { context, defaultPage, target } = options;
19
+ if (target?.page) {
20
+ return target.page;
21
+ }
22
+ if (target?.sessionId) {
23
+ const pageForSession = context.pages().find((candidatePage) => {
24
+ return candidatePage.sessionId() === target.sessionId;
25
+ });
26
+ if (pageForSession) {
27
+ return pageForSession;
28
+ }
29
+ }
30
+ return defaultPage;
31
+ }
32
+ /** Wire onMouseAction. Idempotent. */
33
+ attachToPage(options) {
34
+ const { page } = options;
35
+ if (this.attachedPages.has(page)) {
36
+ return;
37
+ }
38
+ this.attachedPages.add(page);
39
+ if (!this.previousMouseActionByPage.has(page)) {
40
+ this.previousMouseActionByPage.set(page, page.onMouseAction);
41
+ }
42
+ const previousMouseAction = this.previousMouseActionByPage.get(page);
43
+ page.onMouseAction = async (event) => {
44
+ // Ghost cursor must never crash the main Playwright action (click, move, etc).
45
+ // Wrap the entire cursor logic in try/catch so errors stay cosmetic.
46
+ try {
47
+ const pendingCursorApply = this.cursorApplyQueueByPage.get(page) || Promise.resolve();
48
+ const nextCursorApply = pendingCursorApply
49
+ .then(async () => {
50
+ await applyGhostCursorMouseAction({ page, event });
51
+ })
52
+ .catch((error) => {
53
+ if (page.isClosed()) {
54
+ return;
55
+ }
56
+ this.logger.error('[playwriter] Failed to apply ghost cursor action', error);
57
+ });
58
+ this.cursorApplyQueueByPage.set(page, nextCursorApply);
59
+ }
60
+ catch (error) {
61
+ this.logger.error('[playwriter] Ghost cursor onMouseAction error (non-fatal)', error);
62
+ }
63
+ if (!previousMouseAction) {
64
+ return;
65
+ }
66
+ await previousMouseAction(event);
67
+ };
68
+ }
69
+ detachFromPage(options) {
70
+ const { page } = options;
71
+ if (!this.attachedPages.has(page)) {
72
+ return;
73
+ }
74
+ this.attachedPages.delete(page);
75
+ page.onMouseAction = this.previousMouseActionByPage.get(page) ?? null;
76
+ this.previousMouseActionByPage.delete(page);
77
+ this.cursorApplyQueueByPage.delete(page);
78
+ }
79
+ async show(options) {
80
+ try {
81
+ const { page, cursorOptions } = options;
82
+ await enableGhostCursor({ page, cursorOptions });
83
+ }
84
+ catch {
85
+ // Non-fatal — page may be closing or navigating.
86
+ }
87
+ }
88
+ async hide(options) {
89
+ try {
90
+ const { page } = options;
91
+ await disableGhostCursor({ page });
92
+ }
93
+ catch {
94
+ // Non-fatal — page may be closing or navigating.
95
+ }
96
+ }
97
+ }
98
+ //# sourceMappingURL=ghost-cursor-controller.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ghost-cursor-controller.js","sourceRoot":"","sources":["../src/ghost-cursor-controller.ts"],"names":[],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"ghost-cursor.d.ts","sourceRoot":"","sources":["../src/ghost-cursor.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAKH,OAAO,KAAK,EAAE,IAAI,EAAE,gBAAgB,EAAE,MAAM,yBAAyB,CAAA;AAErE,MAAM,WAAW,wBAAwB;IACvC,KAAK,CAAC,EAAE,SAAS,GAAG,KAAK,GAAG,cAAc,CAAA;IAC1C,KAAK,CAAC,EAAE,MAAM,CAAA;IACd,IAAI,CAAC,EAAE,MAAM,CAAA;IACb,MAAM,CAAC,EAAE,MAAM,CAAA;IACf,MAAM,CAAC,EAAE,MAAM,CAAA;IACf,aAAa,CAAC,EAAE,MAAM,CAAA;IACtB,aAAa,CAAC,EAAE,MAAM,CAAA;IACtB,YAAY,CAAC,EAAE,MAAM,CAAA;CACtB;AAmCD,wBAAsB,iBAAiB,CAAC,OAAO,EAAE;IAC/C,IAAI,EAAE,IAAI,CAAA;IACV,aAAa,CAAC,EAAE,wBAAwB,CAAA;CACzC,GAAG,OAAO,CAAC,IAAI,CAAC,CAWhB;AAED,wBAAsB,kBAAkB,CAAC,OAAO,EAAE;IAAE,IAAI,EAAE,IAAI,CAAA;CAAE,GAAG,OAAO,CAAC,IAAI,CAAC,CAM/E;AAED,wBAAsB,2BAA2B,CAAC,OAAO,EAAE;IACzD,IAAI,EAAE,IAAI,CAAA;IACV,KAAK,EAAE,gBAAgB,CAAA;CACxB,GAAG,OAAO,CAAC,IAAI,CAAC,CA4BhB"}
1
+ {"version":3,"file":"ghost-cursor.d.ts","sourceRoot":"","sources":["../src/ghost-cursor.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAKH,OAAO,KAAK,EAAE,IAAI,EAAE,gBAAgB,EAAE,MAAM,yBAAyB,CAAA;AAErE,MAAM,WAAW,wBAAwB;IACvC,KAAK,CAAC,EAAE,SAAS,GAAG,KAAK,GAAG,cAAc,CAAA;IAC1C,KAAK,CAAC,EAAE,MAAM,CAAA;IACd,IAAI,CAAC,EAAE,MAAM,CAAA;IACb,MAAM,CAAC,EAAE,MAAM,CAAA;IACf,MAAM,CAAC,EAAE,MAAM,CAAA;IACf,aAAa,CAAC,EAAE,MAAM,CAAA;IACtB,aAAa,CAAC,EAAE,MAAM,CAAA;IACtB,YAAY,CAAC,EAAE,MAAM,CAAA;CACtB;AAmCD,wBAAsB,iBAAiB,CAAC,OAAO,EAAE;IAC/C,IAAI,EAAE,IAAI,CAAA;IACV,aAAa,CAAC,EAAE,wBAAwB,CAAA;CACzC,GAAG,OAAO,CAAC,IAAI,CAAC,CAehB;AAED,wBAAsB,kBAAkB,CAAC,OAAO,EAAE;IAAE,IAAI,EAAE,IAAI,CAAA;CAAE,GAAG,OAAO,CAAC,IAAI,CAAC,CAU/E;AAED,wBAAsB,2BAA2B,CAAC,OAAO,EAAE;IACzD,IAAI,EAAE,IAAI,CAAA;IACV,KAAK,EAAE,gBAAgB,CAAA;CACxB,GAAG,OAAO,CAAC,IAAI,CAAC,CAiChB"}
@@ -27,37 +27,53 @@ async function ensureGhostCursorInjected(options) {
27
27
  await page.evaluate(code);
28
28
  }
29
29
  export async function enableGhostCursor(options) {
30
- const { page, cursorOptions } = options;
31
- await ensureGhostCursorInjected({ page });
32
- await page.evaluate(({ optionsFromNode }) => {
33
- const api = globalThis.__playwriterGhostCursor;
34
- api?.enable(optionsFromNode);
35
- }, { optionsFromNode: cursorOptions });
30
+ try {
31
+ const { page, cursorOptions } = options;
32
+ await ensureGhostCursorInjected({ page });
33
+ await page.evaluate(({ optionsFromNode }) => {
34
+ const api = globalThis.__playwriterGhostCursor;
35
+ api?.enable(optionsFromNode);
36
+ }, { optionsFromNode: cursorOptions });
37
+ }
38
+ catch {
39
+ // Non-fatal — page may be closed or navigating.
40
+ }
36
41
  }
37
42
  export async function disableGhostCursor(options) {
38
- const { page } = options;
39
- await page.evaluate(() => {
40
- const api = globalThis.__playwriterGhostCursor;
41
- api?.disable();
42
- });
43
+ try {
44
+ const { page } = options;
45
+ await page.evaluate(() => {
46
+ const api = globalThis.__playwriterGhostCursor;
47
+ api?.disable();
48
+ });
49
+ }
50
+ catch {
51
+ // Non-fatal — page may be closed or navigating.
52
+ }
43
53
  }
44
54
  export async function applyGhostCursorMouseAction(options) {
45
- const { page, event } = options;
46
- const applied = await page.evaluate(({ serializedEvent }) => {
47
- const api = globalThis.__playwriterGhostCursor;
48
- if (!api) {
49
- return false;
55
+ // Never throw the cursor is cosmetic and must not break the caller's action.
56
+ try {
57
+ const { page, event } = options;
58
+ const applied = await page.evaluate(({ serializedEvent }) => {
59
+ const api = globalThis.__playwriterGhostCursor;
60
+ if (!api) {
61
+ return false;
62
+ }
63
+ api.applyMouseAction(serializedEvent);
64
+ return true;
65
+ }, { serializedEvent: event });
66
+ if (applied) {
67
+ return;
50
68
  }
51
- api.applyMouseAction(serializedEvent);
52
- return true;
53
- }, { serializedEvent: event });
54
- if (applied) {
55
- return;
69
+ await ensureGhostCursorInjected({ page });
70
+ await page.evaluate(({ serializedEvent }) => {
71
+ const api = globalThis.__playwriterGhostCursor;
72
+ api?.applyMouseAction(serializedEvent);
73
+ }, { serializedEvent: event });
74
+ }
75
+ catch {
76
+ // Swallow — page may be closed, navigating, or debugger detached.
56
77
  }
57
- await ensureGhostCursorInjected({ page });
58
- await page.evaluate(({ serializedEvent }) => {
59
- const api = globalThis.__playwriterGhostCursor;
60
- api?.applyMouseAction(serializedEvent);
61
- }, { serializedEvent: event });
62
78
  }
63
79
  //# sourceMappingURL=ghost-cursor.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"ghost-cursor.js","sourceRoot":"","sources":["../src/ghost-cursor.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"ghost-cursor.js","sourceRoot":"","sources":["../src/ghost-cursor.ts"],"names":[],"mappings":"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"}
package/dist/mcp.d.ts.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"mcp.d.ts","sourceRoot":"","sources":["../src/mcp.ts"],"names":[],"mappings":"AA2VA,wBAAsB,QAAQ,CAAC,OAAO,GAAE;IAAE,IAAI,CAAC,EAAE,MAAM,CAAC;IAAC,KAAK,CAAC,EAAE,MAAM,CAAA;CAAO,iBAuB7E"}
1
+ {"version":3,"file":"mcp.d.ts","sourceRoot":"","sources":["../src/mcp.ts"],"names":[],"mappings":"AAgWA,wBAAsB,QAAQ,CAAC,OAAO,GAAE;IAAE,IAAI,CAAC,EAAE,MAAM,CAAC;IAAC,KAAK,CAAC,EAAE,MAAM,CAAA;CAAO,iBAuB7E"}