@ddwang/magnitude-core 0.3.1-ddwang.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/actions/desktopActions.d.ts +58 -0
- package/dist/actions/desktopActions.js +154 -0
- package/dist/actions/index.d.ts +26 -0
- package/dist/actions/index.js +11 -0
- package/dist/actions/taskActions.d.ts +8 -0
- package/dist/actions/taskActions.js +27 -0
- package/dist/actions/types.d.ts +31 -0
- package/dist/actions/types.js +5 -0
- package/dist/actions/util.d.ts +20 -0
- package/dist/actions/util.js +297 -0
- package/dist/actions/webActions.d.ts +79 -0
- package/dist/actions/webActions.js +190 -0
- package/dist/agent/browserAgent.d.ts +29 -0
- package/dist/agent/browserAgent.js +122 -0
- package/dist/agent/errors.d.ts +8 -0
- package/dist/agent/errors.js +68 -0
- package/dist/agent/index.d.ts +53 -0
- package/dist/agent/index.js +377 -0
- package/dist/agent/narrator.d.ts +4 -0
- package/dist/agent/narrator.js +63 -0
- package/dist/agent/pause.test.d.ts +1 -0
- package/dist/agent/pause.test.js +194 -0
- package/dist/ai/baml_client/async_client.d.ts +73 -0
- package/dist/ai/baml_client/async_client.js +152 -0
- package/dist/ai/baml_client/async_request.d.ts +35 -0
- package/dist/ai/baml_client/async_request.js +98 -0
- package/dist/ai/baml_client/config.d.ts +11 -0
- package/dist/ai/baml_client/config.js +20 -0
- package/dist/ai/baml_client/globals.d.ts +16 -0
- package/dist/ai/baml_client/globals.js +37 -0
- package/dist/ai/baml_client/index.d.ts +16 -0
- package/dist/ai/baml_client/index.js +41 -0
- package/dist/ai/baml_client/inlinedbaml.d.ts +19 -0
- package/dist/ai/baml_client/inlinedbaml.js +31 -0
- package/dist/ai/baml_client/parser.d.ts +53 -0
- package/dist/ai/baml_client/parser.js +86 -0
- package/dist/ai/baml_client/partial_types.d.ts +61 -0
- package/dist/ai/baml_client/partial_types.js +10 -0
- package/dist/ai/baml_client/sync_client.d.ts +50 -0
- package/dist/ai/baml_client/sync_client.js +100 -0
- package/dist/ai/baml_client/sync_request.d.ts +35 -0
- package/dist/ai/baml_client/sync_request.js +98 -0
- package/dist/ai/baml_client/tracing.d.ts +16 -0
- package/dist/ai/baml_client/tracing.js +18 -0
- package/dist/ai/baml_client/type_builder.d.ts +38 -0
- package/dist/ai/baml_client/type_builder.js +96 -0
- package/dist/ai/baml_client/types.d.ts +69 -0
- package/dist/ai/baml_client/types.js +15 -0
- package/dist/ai/claudeCode.d.ts +1 -0
- package/dist/ai/claudeCode.js +186 -0
- package/dist/ai/modelHarness.d.ts +39 -0
- package/dist/ai/modelHarness.js +189 -0
- package/dist/ai/multiModelHarness.d.ts +27 -0
- package/dist/ai/multiModelHarness.js +52 -0
- package/dist/ai/types.d.ts +90 -0
- package/dist/ai/types.js +5 -0
- package/dist/ai/util.d.ts +13 -0
- package/dist/ai/util.js +165 -0
- package/dist/cli.d.ts +0 -0
- package/dist/cli.js +3 -0
- package/dist/common/actions.d.ts +0 -0
- package/dist/common/actions.js +14 -0
- package/dist/common/events.d.ts +15 -0
- package/dist/common/events.js +3 -0
- package/dist/common/failure.d.ts +68 -0
- package/dist/common/failure.js +1 -0
- package/dist/common/index.d.ts +3 -0
- package/dist/common/index.js +3 -0
- package/dist/common/retry.d.ts +16 -0
- package/dist/common/retry.js +30 -0
- package/dist/common/util.d.ts +23 -0
- package/dist/common/util.js +132 -0
- package/dist/connectors/browserConnector.d.ts +40 -0
- package/dist/connectors/browserConnector.js +127 -0
- package/dist/connectors/desktopConnector.d.ts +58 -0
- package/dist/connectors/desktopConnector.js +68 -0
- package/dist/connectors/index.d.ts +10 -0
- package/dist/connectors/index.js +2 -0
- package/dist/index.cjs +6143 -0
- package/dist/index.d.cts +825 -0
- package/dist/index.d.ts +20 -0
- package/dist/index.js +26 -0
- package/dist/index.mjs +6097 -0
- package/dist/logger.d.ts +3 -0
- package/dist/logger.js +15 -0
- package/dist/memory/agentMemory.d.ts +37 -0
- package/dist/memory/agentMemory.js +121 -0
- package/dist/memory/image.d.ts +26 -0
- package/dist/memory/image.js +106 -0
- package/dist/memory/index.d.ts +3 -0
- package/dist/memory/index.js +3 -0
- package/dist/memory/masking.d.ts +7 -0
- package/dist/memory/masking.js +141 -0
- package/dist/memory/masking.test.d.ts +1 -0
- package/dist/memory/masking.test.js +117 -0
- package/dist/memory/observation.d.ts +38 -0
- package/dist/memory/observation.js +79 -0
- package/dist/memory/rendering/index.d.ts +10 -0
- package/dist/memory/rendering/index.js +13 -0
- package/dist/memory/rendering/renderJsonParts.d.ts +3 -0
- package/dist/memory/rendering/renderJsonParts.js +115 -0
- package/dist/memory/rendering/renderJsonParts.test.d.ts +1 -0
- package/dist/memory/rendering/renderJsonParts.test.js +217 -0
- package/dist/memory/rendering/renderXmlParts.d.ts +7 -0
- package/dist/memory/rendering/renderXmlParts.js +127 -0
- package/dist/memory/serde.d.ts +20 -0
- package/dist/memory/serde.js +81 -0
- package/dist/memory/util.d.ts +2 -0
- package/dist/memory/util.js +17 -0
- package/dist/telemetry/events.d.ts +9 -0
- package/dist/telemetry/events.js +80 -0
- package/dist/telemetry/index.d.ts +6 -0
- package/dist/telemetry/index.js +91 -0
- package/dist/types.d.ts +30 -0
- package/dist/types.js +1 -0
- package/dist/util.d.ts +1 -0
- package/dist/util.js +12 -0
- package/dist/version.d.ts +1 -0
- package/dist/version.js +3 -0
- package/dist/web/browserProvider.d.ts +23 -0
- package/dist/web/browserProvider.js +153 -0
- package/dist/web/harness.d.ts +102 -0
- package/dist/web/harness.js +340 -0
- package/dist/web/stability.d.ts +57 -0
- package/dist/web/stability.js +316 -0
- package/dist/web/tabs.d.ts +58 -0
- package/dist/web/tabs.js +293 -0
- package/dist/web/transformer.d.ts +7 -0
- package/dist/web/transformer.js +41 -0
- package/dist/web/types.d.ts +32 -0
- package/dist/web/types.js +2 -0
- package/dist/web/util.d.ts +3 -0
- package/dist/web/util.js +81 -0
- package/dist/web/visualizer/cursor.d.ts +16 -0
- package/dist/web/visualizer/cursor.js +152 -0
- package/dist/web/visualizer/index.d.ts +22 -0
- package/dist/web/visualizer/index.js +47 -0
- package/dist/web/visualizer/mouseEffects.d.ts +18 -0
- package/dist/web/visualizer/mouseEffects.js +287 -0
- package/dist/web/visualizer/typeEffects.d.ts +10 -0
- package/dist/web/visualizer/typeEffects.js +190 -0
- package/package.json +71 -0
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import getShadowDOMInputAdapterScript from './scripts/shadowDOMInputAdapter';
|
|
2
|
+
import logger from '@/logger';
|
|
3
|
+
export class DOMTransformer {
|
|
4
|
+
initializedPages = new WeakSet(); // Track pages for which 'load' listener is set
|
|
5
|
+
constructor() { }
|
|
6
|
+
setActivePage(newPage) {
|
|
7
|
+
// Only add the 'load' listener if we haven't done so for this specific Page object instance.
|
|
8
|
+
if (!this.initializedPages.has(newPage)) {
|
|
9
|
+
newPage.on('load', async () => {
|
|
10
|
+
// Pass 'newPage' (the page that triggered the 'load' event) to setupScriptForPage.
|
|
11
|
+
await this.setupScriptForPage(newPage);
|
|
12
|
+
});
|
|
13
|
+
this.initializedPages.add(newPage); // Mark this Page object as having its 'load' listener set up.
|
|
14
|
+
}
|
|
15
|
+
}
|
|
16
|
+
async setupScriptForPage(targetPage) {
|
|
17
|
+
try {
|
|
18
|
+
// Check if a marker for the script already exists on the page for this load cycle.
|
|
19
|
+
const scriptAlreadyInjected = await targetPage.evaluate(() => {
|
|
20
|
+
return window.__magnitudeShadowDOMAdapterInjected === true;
|
|
21
|
+
}).catch(() => false); // If evaluate fails (e.g., page closed), assume not injected.
|
|
22
|
+
if (scriptAlreadyInjected) {
|
|
23
|
+
logger.trace('Select manager script already present on this page load.');
|
|
24
|
+
return;
|
|
25
|
+
}
|
|
26
|
+
// Get the script as a string from the separate file
|
|
27
|
+
const scriptFnString = getShadowDOMInputAdapterScript();
|
|
28
|
+
// Evaluate the script function in the browser and set the marker.
|
|
29
|
+
// We need to wrap it in a self-executing function
|
|
30
|
+
await targetPage.evaluate(`
|
|
31
|
+
(${scriptFnString})();
|
|
32
|
+
window.__magnitudeShadowDOMAdapterInjected = true;
|
|
33
|
+
`);
|
|
34
|
+
logger.trace(`Script injected into page: ${targetPage.url()}`);
|
|
35
|
+
}
|
|
36
|
+
catch (error) {
|
|
37
|
+
const url = targetPage.isClosed() ? '[closed page]' : targetPage.url();
|
|
38
|
+
logger.warn(`Error injecting script into ${url}: ${error.message}`);
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
export type Base64Image = `data:image/${'jpeg' | 'png' | 'gif'};base64,${string}`;
|
|
2
|
+
export type WebAction = NavigateWebAction | ClickWebAction | TypeWebAction | ScrollWebAction | SwitchTabWebAction;
|
|
3
|
+
export interface NavigateWebAction {
|
|
4
|
+
variant: 'load';
|
|
5
|
+
url: string;
|
|
6
|
+
}
|
|
7
|
+
export interface ClickWebAction {
|
|
8
|
+
variant: 'click';
|
|
9
|
+
x: number;
|
|
10
|
+
y: number;
|
|
11
|
+
}
|
|
12
|
+
export interface TypeWebAction {
|
|
13
|
+
variant: 'type';
|
|
14
|
+
x: number;
|
|
15
|
+
y: number;
|
|
16
|
+
content: string;
|
|
17
|
+
}
|
|
18
|
+
export interface ScrollWebAction {
|
|
19
|
+
variant: 'scroll';
|
|
20
|
+
x: number;
|
|
21
|
+
y: number;
|
|
22
|
+
deltaX: number;
|
|
23
|
+
deltaY: number;
|
|
24
|
+
}
|
|
25
|
+
export type SwitchTabWebAction = {
|
|
26
|
+
variant: 'tab';
|
|
27
|
+
index: number;
|
|
28
|
+
};
|
|
29
|
+
export interface PixelCoordinate {
|
|
30
|
+
x: number;
|
|
31
|
+
y: number;
|
|
32
|
+
}
|
package/dist/web/util.js
ADDED
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
export function parseTypeContent(content) {
|
|
2
|
+
// Parse content sequence potentially with special indicators <enter> or <tab> into components
|
|
3
|
+
const regex = /<enter>|<tab>/g;
|
|
4
|
+
const result = [];
|
|
5
|
+
let lastIndex = 0;
|
|
6
|
+
content.replace(regex, (match, offset) => {
|
|
7
|
+
// Grab the text before the match and trim surrounding whitespace
|
|
8
|
+
const text = content.slice(lastIndex, offset).trim();
|
|
9
|
+
if (text)
|
|
10
|
+
result.push(text);
|
|
11
|
+
// Add the matched special indicator
|
|
12
|
+
result.push(match);
|
|
13
|
+
lastIndex = offset + match.length;
|
|
14
|
+
return match;
|
|
15
|
+
});
|
|
16
|
+
// Add any remaining text after the last match
|
|
17
|
+
const remaining = content.slice(lastIndex).trim();
|
|
18
|
+
if (remaining)
|
|
19
|
+
result.push(remaining);
|
|
20
|
+
return result;
|
|
21
|
+
}
|
|
22
|
+
export function renderMinimalAccessibilityTree(node) {
|
|
23
|
+
// Escapes newlines for safe inline printing, and trims whitespace.
|
|
24
|
+
const sanitize = (text) => {
|
|
25
|
+
return String(text).trim().replace(/\n/g, '\\n');
|
|
26
|
+
};
|
|
27
|
+
// Inner recursive function to handle state like indentation
|
|
28
|
+
function recursiveFlatten(node, indent) {
|
|
29
|
+
const isTextLike = (n) => n && (n.role === 'StaticText' || n.role === 'text') && n.name && n.name.trim();
|
|
30
|
+
let childrenOutput = '';
|
|
31
|
+
if (node.children) {
|
|
32
|
+
const newChildren = [];
|
|
33
|
+
let currentTextBuffer = [];
|
|
34
|
+
for (const child of node.children) {
|
|
35
|
+
if (isTextLike(child)) {
|
|
36
|
+
currentTextBuffer.push(sanitize(child.name));
|
|
37
|
+
}
|
|
38
|
+
else {
|
|
39
|
+
if (currentTextBuffer.length > 0) {
|
|
40
|
+
newChildren.push({ role: 'coalesced-text', name: currentTextBuffer.join(' ') });
|
|
41
|
+
currentTextBuffer = [];
|
|
42
|
+
}
|
|
43
|
+
newChildren.push(child);
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
if (currentTextBuffer.length > 0) {
|
|
47
|
+
newChildren.push({ role: 'coalesced-text', name: currentTextBuffer.join(' ') });
|
|
48
|
+
}
|
|
49
|
+
for (const child of newChildren) {
|
|
50
|
+
if (child.role === 'coalesced-text') {
|
|
51
|
+
childrenOutput += `${indent} ${child.name}\n`;
|
|
52
|
+
}
|
|
53
|
+
else {
|
|
54
|
+
childrenOutput += recursiveFlatten(child, indent + ' ');
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
const isBoringContainer = (node.role === 'generic' || node.role === 'div') && !node.name;
|
|
59
|
+
if (isBoringContainer) {
|
|
60
|
+
return childrenOutput;
|
|
61
|
+
}
|
|
62
|
+
let line = '';
|
|
63
|
+
if (node.role && !['StaticText', 'text', 'RootWebArea'].includes(node.role)) {
|
|
64
|
+
line = `${indent}[${node.role}]`;
|
|
65
|
+
if (node.name && sanitize(node.name)) {
|
|
66
|
+
line += ` ${sanitize(node.name)}`;
|
|
67
|
+
}
|
|
68
|
+
if (node.value !== undefined && sanitize(node.value)) {
|
|
69
|
+
line += ` (value: ${sanitize(node.value)})`;
|
|
70
|
+
}
|
|
71
|
+
if (node.checked !== undefined)
|
|
72
|
+
line += ` (checked: ${node.checked})`;
|
|
73
|
+
if (node.disabled)
|
|
74
|
+
line += ` (disabled)`;
|
|
75
|
+
line += '\n';
|
|
76
|
+
}
|
|
77
|
+
return line + childrenOutput;
|
|
78
|
+
}
|
|
79
|
+
const rawFlattened = recursiveFlatten(node, '');
|
|
80
|
+
return rawFlattened.split('\n').filter(line => line.trim() !== '').join('\n');
|
|
81
|
+
}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { Page } from "playwright";
|
|
2
|
+
export declare class CursorVisual {
|
|
3
|
+
/**
|
|
4
|
+
* Manages the visual indicator for actions on a page
|
|
5
|
+
*/
|
|
6
|
+
private page;
|
|
7
|
+
private visualElementId;
|
|
8
|
+
private lastPosition;
|
|
9
|
+
constructor();
|
|
10
|
+
setActivePage(page: Page): Promise<void>;
|
|
11
|
+
move(x: number, y: number): Promise<void>;
|
|
12
|
+
setupOnPage(): Promise<void>;
|
|
13
|
+
private _drawVisual;
|
|
14
|
+
hide(): Promise<void>;
|
|
15
|
+
show(): Promise<void>;
|
|
16
|
+
}
|
|
@@ -0,0 +1,152 @@
|
|
|
1
|
+
import { retryOnErrorIsSuccess } from "@/common";
|
|
2
|
+
import logger from "@/logger";
|
|
3
|
+
export class CursorVisual {
|
|
4
|
+
/**
|
|
5
|
+
* Manages the visual indicator for actions on a page
|
|
6
|
+
*/
|
|
7
|
+
page;
|
|
8
|
+
visualElementId = 'action-visual-indicator';
|
|
9
|
+
lastPosition = null;
|
|
10
|
+
constructor() {
|
|
11
|
+
//this.page = page;
|
|
12
|
+
}
|
|
13
|
+
async setActivePage(page) {
|
|
14
|
+
this.page = page;
|
|
15
|
+
page.on('load', async () => {
|
|
16
|
+
await retryOnErrorIsSuccess(this.setupOnPage.bind(this), { mode: 'retry_all', delayMs: 200, retryLimit: 10 });
|
|
17
|
+
});
|
|
18
|
+
await retryOnErrorIsSuccess(this.setupOnPage.bind(this), { mode: 'retry_all', delayMs: 200, retryLimit: 5 });
|
|
19
|
+
}
|
|
20
|
+
async move(x, y) {
|
|
21
|
+
// Store the position
|
|
22
|
+
this.lastPosition = { x, y };
|
|
23
|
+
// Create or update the mouse pointer visual, showing the click effect
|
|
24
|
+
await this._drawVisual(x, y, false);
|
|
25
|
+
// The pointer visual takes 0.3s on the transition, but awaiting script evaluation does not wait for this to complete.
|
|
26
|
+
// So we wait 300ms manually.
|
|
27
|
+
await this.page.waitForTimeout(300);
|
|
28
|
+
}
|
|
29
|
+
async setupOnPage() {
|
|
30
|
+
if (this.lastPosition) {
|
|
31
|
+
// Redraw the visual without the click effect
|
|
32
|
+
await this._drawVisual(this.lastPosition.x, this.lastPosition.y, false);
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
// Internal method to handle the actual drawing logic
|
|
36
|
+
async _drawVisual(x, y, showClickEffect) {
|
|
37
|
+
try {
|
|
38
|
+
await this.page.evaluate(({ x, y, id, showClickEffect }) => {
|
|
39
|
+
// Use viewport coordinates directly (no scroll adjustment for fixed positioning)
|
|
40
|
+
const viewportX = x;
|
|
41
|
+
const viewportY = y;
|
|
42
|
+
// Document coordinates for the click effect circle (which uses absolute positioning)
|
|
43
|
+
const docX = x + window.scrollX;
|
|
44
|
+
const docY = y + window.scrollY;
|
|
45
|
+
// --- Create Expanding/Fading Circle (Optional) ---
|
|
46
|
+
if (showClickEffect) {
|
|
47
|
+
const circle = document.createElement('div');
|
|
48
|
+
circle.style.position = 'absolute';
|
|
49
|
+
circle.style.left = `${docX}px`;
|
|
50
|
+
circle.style.top = `${docY}px`;
|
|
51
|
+
circle.style.borderRadius = '50%';
|
|
52
|
+
circle.style.backgroundColor = '#026aa1'; // Blue color
|
|
53
|
+
circle.style.width = '0px';
|
|
54
|
+
circle.style.height = '0px';
|
|
55
|
+
circle.style.transform = 'translate(-50%, -50%)'; // Center on (x, y)
|
|
56
|
+
circle.style.pointerEvents = 'none';
|
|
57
|
+
circle.style.zIndex = '9998'; // Below the pointer
|
|
58
|
+
circle.style.opacity = '0.7'; // Initial opacity
|
|
59
|
+
document.body.appendChild(circle);
|
|
60
|
+
// Animate the circle
|
|
61
|
+
const animation = circle.animate([
|
|
62
|
+
{ width: '0px', height: '0px', opacity: 0.7 }, // Start state
|
|
63
|
+
{ width: '50px', height: '50px', opacity: 0 } // End state
|
|
64
|
+
], {
|
|
65
|
+
duration: 500, // 500ms duration
|
|
66
|
+
easing: 'ease-out'
|
|
67
|
+
});
|
|
68
|
+
// Remove circle after animation
|
|
69
|
+
animation.onfinish = () => {
|
|
70
|
+
circle.remove();
|
|
71
|
+
};
|
|
72
|
+
}
|
|
73
|
+
// --- Pointer Logic (Always runs) ---
|
|
74
|
+
// Check if the visual indicator already exists
|
|
75
|
+
let pointerElement = document.getElementById(id);
|
|
76
|
+
// If it doesn't exist, create it with all necessary styling
|
|
77
|
+
if (!pointerElement) {
|
|
78
|
+
pointerElement = document.createElement('div');
|
|
79
|
+
pointerElement.id = id;
|
|
80
|
+
pointerElement.style.position = 'fixed'; // Use fixed positioning for viewport-relative
|
|
81
|
+
pointerElement.style.width = '32px';
|
|
82
|
+
pointerElement.style.height = '32px';
|
|
83
|
+
pointerElement.style.zIndex = '2147483647'; // Max z-index
|
|
84
|
+
pointerElement.style.pointerEvents = 'none'; // Don't interfere with actual clicks
|
|
85
|
+
// Notice that transition is 300ms
|
|
86
|
+
pointerElement.style.transition = 'left 0.3s cubic-bezier(0.25, 0.1, 0.25, 1), top 0.3s cubic-bezier(0.25, 0.1, 0.25, 1)';
|
|
87
|
+
// Create SVG using DOM methods to avoid Trusted Types issues
|
|
88
|
+
const svg = document.createElementNS('http://www.w3.org/2000/svg', 'svg');
|
|
89
|
+
svg.setAttribute('width', '32');
|
|
90
|
+
svg.setAttribute('height', '32');
|
|
91
|
+
svg.setAttribute('viewBox', '0 0 113.50408 99.837555');
|
|
92
|
+
const g = document.createElementNS('http://www.w3.org/2000/svg', 'g');
|
|
93
|
+
g.setAttribute('transform', 'translate(-413.10686,-501.19661)');
|
|
94
|
+
const path1 = document.createElementNS('http://www.w3.org/2000/svg', 'path');
|
|
95
|
+
path1.setAttribute('style', 'fill:#026aa1;fill-opacity:1;stroke:#000000;stroke-width:0');
|
|
96
|
+
path1.setAttribute('d', 'm 416.1069,504.1966 52.47697,93.83813 8.33253,-57.61019 z');
|
|
97
|
+
const path2 = document.createElementNS('http://www.w3.org/2000/svg', 'path');
|
|
98
|
+
path2.setAttribute('style', 'fill:#0384c7;fill-opacity:1;stroke:#000000;stroke-width:0');
|
|
99
|
+
path2.setAttribute('d', 'm 416.1069,504.1966 60.8095,36.22794 46.69517,-34.75524 z');
|
|
100
|
+
const path3 = document.createElementNS('http://www.w3.org/2000/svg', 'path');
|
|
101
|
+
path3.setAttribute('style', 'fill:#0384c7;fill-opacity:0;stroke:#000000;stroke-width:6;stroke-linecap:round;stroke-linejoin:round');
|
|
102
|
+
path3.setAttribute('d', 'm 416.1069,504.19658 52.47698,93.83813 8.33252,-57.61019 46.69517,-34.75521 -107.50467,-1.47273');
|
|
103
|
+
g.appendChild(path1);
|
|
104
|
+
g.appendChild(path2);
|
|
105
|
+
g.appendChild(path3);
|
|
106
|
+
svg.appendChild(g);
|
|
107
|
+
pointerElement.appendChild(svg);
|
|
108
|
+
document.body.appendChild(pointerElement);
|
|
109
|
+
}
|
|
110
|
+
//pointerElement.style.display = 'none';
|
|
111
|
+
// Update position - use viewport coordinates for fixed positioning
|
|
112
|
+
// Set the top-left corner to (viewportX, viewportY) and then translate by (-1px, -3px)
|
|
113
|
+
// to align the pointer tip (approx. at 1.27, 4.17 within the SVG) with the click point.
|
|
114
|
+
pointerElement.style.left = `${viewportX}px`;
|
|
115
|
+
pointerElement.style.top = `${viewportY}px`;
|
|
116
|
+
pointerElement.style.transform = 'translate(-1px, -3px)';
|
|
117
|
+
}, { x, y, id: this.visualElementId, showClickEffect });
|
|
118
|
+
}
|
|
119
|
+
catch (error) {
|
|
120
|
+
// For example when:
|
|
121
|
+
// TypeError: Failed to set the 'innerHTML' property on 'Element': This document requires 'TrustedHTML' assignment.
|
|
122
|
+
logger.trace(`Failed to draw visual: ${error.message}`);
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
async hide() {
|
|
126
|
+
try {
|
|
127
|
+
await this.page.evaluate((id) => {
|
|
128
|
+
const element = document.getElementById(id);
|
|
129
|
+
if (element) {
|
|
130
|
+
element.style.display = 'none';
|
|
131
|
+
}
|
|
132
|
+
}, this.visualElementId);
|
|
133
|
+
}
|
|
134
|
+
catch {
|
|
135
|
+
logger.trace(`Failed to hide pointer`);
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
async show() {
|
|
139
|
+
try {
|
|
140
|
+
await this.page.evaluate((id) => {
|
|
141
|
+
const element = document.getElementById(id);
|
|
142
|
+
if (element) {
|
|
143
|
+
// Revert to the default display value (usually 'block' for a div)
|
|
144
|
+
element.style.display = '';
|
|
145
|
+
}
|
|
146
|
+
}, this.visualElementId);
|
|
147
|
+
}
|
|
148
|
+
catch {
|
|
149
|
+
logger.trace(`Failed to show pointer`);
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import { BrowserContext, Page } from "playwright";
|
|
2
|
+
export interface ActionVisualizerOptions {
|
|
3
|
+
showCursor?: boolean;
|
|
4
|
+
showHoverCircle?: boolean;
|
|
5
|
+
showClickRipple?: boolean;
|
|
6
|
+
showDragLine?: boolean;
|
|
7
|
+
showTypeEffects?: boolean;
|
|
8
|
+
}
|
|
9
|
+
export declare class ActionVisualizer {
|
|
10
|
+
private options;
|
|
11
|
+
private context;
|
|
12
|
+
private page;
|
|
13
|
+
private cursor;
|
|
14
|
+
private mouseEffects;
|
|
15
|
+
private typeEffects;
|
|
16
|
+
constructor(context: BrowserContext, options: ActionVisualizerOptions);
|
|
17
|
+
setup(): Promise<void>;
|
|
18
|
+
setActivePage(page: Page): Promise<void>;
|
|
19
|
+
moveVirtualCursor(x: number, y: number): Promise<void>;
|
|
20
|
+
hideAll(): Promise<void>;
|
|
21
|
+
showAll(): Promise<void>;
|
|
22
|
+
}
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
import { CursorVisual } from "./cursor";
|
|
2
|
+
import { MouseEffectVisual } from "./mouseEffects";
|
|
3
|
+
import { TypeEffectVisual } from "./typeEffects";
|
|
4
|
+
export class ActionVisualizer {
|
|
5
|
+
options;
|
|
6
|
+
context;
|
|
7
|
+
page;
|
|
8
|
+
cursor;
|
|
9
|
+
mouseEffects;
|
|
10
|
+
typeEffects;
|
|
11
|
+
constructor(context, options) {
|
|
12
|
+
this.context = context;
|
|
13
|
+
this.options = {
|
|
14
|
+
showCursor: options.showCursor ?? true,
|
|
15
|
+
showHoverCircle: options.showHoverCircle ?? false,
|
|
16
|
+
showClickRipple: options.showClickRipple ?? true,
|
|
17
|
+
showDragLine: options.showDragLine ?? false,
|
|
18
|
+
showTypeEffects: options.showTypeEffects ?? true
|
|
19
|
+
};
|
|
20
|
+
this.cursor = new CursorVisual();
|
|
21
|
+
this.mouseEffects = new MouseEffectVisual(this.options);
|
|
22
|
+
this.typeEffects = new TypeEffectVisual();
|
|
23
|
+
}
|
|
24
|
+
async setup() {
|
|
25
|
+
// await this.mouseEffects.setContext(this.context);
|
|
26
|
+
// if (this.options.showTypeEffects) {
|
|
27
|
+
// await this.typeEffects.setContext(this.context);
|
|
28
|
+
// }
|
|
29
|
+
//this.showAll();
|
|
30
|
+
}
|
|
31
|
+
async setActivePage(page) {
|
|
32
|
+
this.page = page;
|
|
33
|
+
await this.cursor.setActivePage(page);
|
|
34
|
+
await this.mouseEffects.setActivePage(page);
|
|
35
|
+
await this.typeEffects.setActivePage(page);
|
|
36
|
+
}
|
|
37
|
+
async moveVirtualCursor(x, y) {
|
|
38
|
+
// Takes like 300ms for smooth anim
|
|
39
|
+
await this.cursor.move(x, y);
|
|
40
|
+
}
|
|
41
|
+
async hideAll() {
|
|
42
|
+
await this.cursor.hide();
|
|
43
|
+
}
|
|
44
|
+
async showAll() {
|
|
45
|
+
await this.cursor.show();
|
|
46
|
+
}
|
|
47
|
+
}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { Page } from "playwright";
|
|
2
|
+
interface MouseEffectOptions {
|
|
3
|
+
baseOpacity?: number;
|
|
4
|
+
showHoverCircle?: boolean;
|
|
5
|
+
showClickRipple?: boolean;
|
|
6
|
+
showDragLine?: boolean;
|
|
7
|
+
}
|
|
8
|
+
export declare class MouseEffectVisual {
|
|
9
|
+
private baseOpacity;
|
|
10
|
+
private showHoverCircle;
|
|
11
|
+
private showClickRipple;
|
|
12
|
+
private showDragLine;
|
|
13
|
+
private page;
|
|
14
|
+
constructor(options?: MouseEffectOptions);
|
|
15
|
+
setActivePage(page: Page): Promise<void>;
|
|
16
|
+
setupOnPage(): Promise<void>;
|
|
17
|
+
}
|
|
18
|
+
export {};
|