testdriverai 6.2.2 → 7.1.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.
- package/.github/workflows/acceptance-linux.yml +75 -0
- package/.github/workflows/acceptance-sdk-tests.yml +133 -0
- package/.vscode/settings.json +5 -1
- package/AGENTS.md +550 -0
- package/CODEOWNERS +0 -1
- package/README.md +126 -0
- package/{testdriver → _testdriver}/acceptance/drag-and-drop.yaml +2 -2
- package/{testdriver → _testdriver}/acceptance/snippets/login.yaml +1 -1
- package/_testdriver/examples/desktop/lifecycle/prerun.yaml +0 -0
- package/{testdriver → _testdriver}/examples/web/lifecycle/prerun.yaml +6 -1
- package/{testdriver → _testdriver}/lifecycle/postrun.yaml +3 -2
- package/_testdriver/lifecycle/prerun.yaml +15 -0
- package/{testdriver → _testdriver}/lifecycle/provision.yaml +7 -2
- package/agent/index.js +300 -85
- package/agent/interface.js +15 -0
- package/agent/lib/cache.js +142 -0
- package/agent/lib/commander.js +1 -39
- package/agent/lib/commands.js +910 -296
- package/agent/lib/redraw.js +129 -41
- package/agent/lib/sandbox.js +29 -6
- package/agent/lib/sdk.js +22 -0
- package/agent/lib/system.js +0 -3
- package/agent/lib/validation.js +1 -7
- package/debug-locate-response.js +82 -0
- package/debugger/index.html +15 -4
- package/docs/ARCHITECTURE.md +424 -0
- package/docs/AWESOME_LOGS_QUICK_REF.md +100 -0
- package/docs/MIGRATION.md +425 -0
- package/docs/PRESETS.md +210 -0
- package/docs/QUICK_START_TEST_RECORDING.md +215 -0
- package/docs/SDK_AWESOME_LOGS.md +468 -0
- package/docs/TEST_RECORDING.md +388 -0
- package/docs/docs.json +286 -152
- package/docs/guide/best-practices-polling.mdx +154 -0
- package/docs/sdk-browser-rendering.md +167 -0
- package/docs/v6/getting-started/self-hosting.mdx +407 -0
- package/docs/{guide → v6/guide}/dashcam.mdx +1 -1
- package/docs/{guide → v6/guide}/environment-variables.mdx +4 -5
- package/docs/{guide → v6/guide}/lifecycle.mdx +1 -1
- package/docs/v6/overview/comparison.mdx +101 -0
- package/docs/v7/README.md +135 -0
- package/docs/v7/api/ai.mdx +205 -0
- package/docs/v7/api/assert.mdx +285 -0
- package/docs/v7/api/assertions.mdx +403 -0
- package/docs/v7/api/click.mdx +287 -0
- package/docs/v7/api/client.mdx +322 -0
- package/docs/v7/api/dashcam.mdx +497 -0
- package/docs/v7/api/doubleClick.mdx +102 -0
- package/docs/v7/api/elements.mdx +479 -0
- package/docs/v7/api/exec.mdx +346 -0
- package/docs/v7/api/find.mdx +316 -0
- package/docs/v7/api/focusApplication.mdx +294 -0
- package/docs/v7/api/hover.mdx +279 -0
- package/docs/v7/api/mouseDown.mdx +161 -0
- package/docs/v7/api/mouseUp.mdx +164 -0
- package/docs/v7/api/pressKeys.mdx +349 -0
- package/docs/v7/api/rightClick.mdx +123 -0
- package/docs/v7/api/sandbox.mdx +404 -0
- package/docs/v7/api/scroll.mdx +300 -0
- package/docs/v7/api/type.mdx +314 -0
- package/docs/v7/commands/assert.mdx +45 -0
- package/docs/v7/commands/exec.mdx +282 -0
- package/docs/v7/commands/focus-application.mdx +44 -0
- package/docs/v7/commands/hover-image.mdx +69 -0
- package/docs/v7/commands/hover-text.mdx +47 -0
- package/docs/v7/commands/if.mdx +53 -0
- package/docs/v7/commands/match-image.mdx +67 -0
- package/docs/v7/commands/press-keys.mdx +87 -0
- package/docs/v7/commands/remember.mdx +49 -0
- package/docs/v7/commands/run.mdx +44 -0
- package/docs/v7/commands/scroll-until-image.mdx +66 -0
- package/docs/v7/commands/scroll-until-text.mdx +60 -0
- package/docs/v7/commands/scroll.mdx +69 -0
- package/docs/v7/commands/type.mdx +45 -0
- package/docs/v7/commands/wait-for-image.mdx +54 -0
- package/docs/v7/commands/wait-for-text.mdx +48 -0
- package/docs/v7/commands/wait.mdx +45 -0
- package/docs/v7/getting-started/configuration.mdx +380 -0
- package/docs/v7/getting-started/quickstart.mdx +332 -0
- package/docs/v7/guides/best-practices.mdx +486 -0
- package/docs/v7/guides/caching-ai.mdx +215 -0
- package/docs/v7/guides/caching-selectors.mdx +292 -0
- package/docs/v7/guides/caching.mdx +366 -0
- package/docs/v7/guides/ci-cd/azure.mdx +587 -0
- package/docs/v7/guides/ci-cd/circleci.mdx +523 -0
- package/docs/v7/guides/ci-cd/github-actions.mdx +457 -0
- package/docs/v7/guides/ci-cd/gitlab.mdx +498 -0
- package/docs/v7/guides/ci-cd/jenkins.mdx +664 -0
- package/docs/v7/guides/ci-cd/travis.mdx +438 -0
- package/docs/v7/guides/debugging.mdx +349 -0
- package/docs/v7/guides/faq.mdx +393 -0
- package/docs/v7/guides/migration.mdx +562 -0
- package/docs/v7/guides/performance.mdx +517 -0
- package/docs/{getting-started → v7/guides}/self-hosting.mdx +11 -12
- package/docs/v7/guides/troubleshooting.mdx +526 -0
- package/docs/v7/guides/vitest-plugin.mdx +477 -0
- package/docs/v7/guides/vitest.mdx +535 -0
- package/docs/v7/platforms/linux.mdx +308 -0
- package/docs/v7/platforms/macos.mdx +433 -0
- package/docs/v7/platforms/windows.mdx +430 -0
- package/docs/v7/playwright.mdx +342 -0
- package/docs/v7/presets/chrome-extension.mdx +223 -0
- package/docs/v7/presets/chrome.mdx +287 -0
- package/docs/v7/presets/electron.mdx +435 -0
- package/docs/v7/presets/vscode.mdx +398 -0
- package/docs/v7/presets/webapp.mdx +396 -0
- package/docs/v7/progressive-apis/CORE.md +459 -0
- package/docs/v7/progressive-apis/HOOKS.md +360 -0
- package/docs/v7/progressive-apis/PROGRESSIVE_DISCLOSURE.md +230 -0
- package/docs/v7/progressive-apis/PROVISION.md +266 -0
- package/eslint.config.js +19 -1
- package/interfaces/cli/lib/base.js +10 -4
- package/interfaces/logger.js +2 -1
- package/interfaces/shared-test-state.mjs +69 -0
- package/interfaces/vitest-plugin.mjs +830 -0
- package/package.json +29 -5
- package/schema.json +8 -29
- package/scripts/view-test-results.mjs +96 -0
- package/sdk-log-formatter.js +714 -0
- package/sdk.d.ts +1028 -0
- package/sdk.js +2567 -0
- package/{.github/workflows/self-hosted.yml → self-hosted.yml} +13 -4
- package/setup/aws/cloudformation.yaml +9 -2
- package/src/core/Dashcam.js +469 -0
- package/src/core/index.d.ts +150 -0
- package/src/core/index.js +12 -0
- package/src/presets/index.mjs +331 -0
- package/src/vitest/extended.mjs +108 -0
- package/src/vitest/hooks.d.ts +119 -0
- package/src/vitest/hooks.mjs +298 -0
- package/src/vitest/index.mjs +64 -0
- package/src/vitest/lifecycle.mjs +277 -0
- package/src/vitest/utils.mjs +150 -0
- package/test/dashcam.test.js +137 -0
- package/test/mcp-example-test.yaml +27 -0
- package/testdriver/acceptance-sdk/QUICK_REFERENCE.md +61 -0
- package/testdriver/acceptance-sdk/README.md +128 -0
- package/testdriver/acceptance-sdk/TEST_REPORTING.md +245 -0
- package/testdriver/acceptance-sdk/assert.test.mjs +26 -0
- package/testdriver/acceptance-sdk/auto-cache-key-demo.test.mjs +56 -0
- package/testdriver/acceptance-sdk/chrome-extension.test.mjs +89 -0
- package/testdriver/acceptance-sdk/drag-and-drop.test.mjs +58 -0
- package/testdriver/acceptance-sdk/element-not-found.test.mjs +25 -0
- package/testdriver/acceptance-sdk/exec-js.test.mjs +43 -0
- package/testdriver/acceptance-sdk/exec-output.test.mjs +59 -0
- package/testdriver/acceptance-sdk/exec-pwsh.test.mjs +57 -0
- package/testdriver/acceptance-sdk/focus-window.test.mjs +36 -0
- package/testdriver/acceptance-sdk/formatted-logging.test.mjs +26 -0
- package/testdriver/acceptance-sdk/hooks-example.test.mjs +38 -0
- package/testdriver/acceptance-sdk/hover-image.test.mjs +34 -0
- package/testdriver/acceptance-sdk/hover-text-with-description.test.mjs +38 -0
- package/testdriver/acceptance-sdk/hover-text.test.mjs +27 -0
- package/testdriver/acceptance-sdk/match-image.test.mjs +36 -0
- package/testdriver/acceptance-sdk/presets-example.test.mjs +87 -0
- package/testdriver/acceptance-sdk/press-keys.test.mjs +50 -0
- package/testdriver/acceptance-sdk/prompt.test.mjs +33 -0
- package/testdriver/acceptance-sdk/scroll-keyboard.test.mjs +38 -0
- package/testdriver/acceptance-sdk/scroll-until-image.test.mjs +39 -0
- package/testdriver/acceptance-sdk/scroll-until-text.test.mjs +28 -0
- package/testdriver/acceptance-sdk/scroll.test.mjs +41 -0
- package/testdriver/acceptance-sdk/setup/globalTeardown.mjs +11 -0
- package/testdriver/acceptance-sdk/setup/testHelpers.mjs +420 -0
- package/testdriver/acceptance-sdk/setup/vitestSetup.mjs +40 -0
- package/testdriver/acceptance-sdk/sully-ai.test.mjs +234 -0
- package/testdriver/acceptance-sdk/test-console-logs.test.mjs +42 -0
- package/testdriver/acceptance-sdk/type-checking-demo.js +49 -0
- package/testdriver/acceptance-sdk/type.test.mjs +45 -0
- package/verify-element-api.js +89 -0
- package/verify-types.js +0 -0
- package/vitest.config.example.js +19 -0
- package/vitest.config.mjs +66 -0
- package/vitest.config.mjs.bak +44 -0
- package/.github/workflows/acceptance-v6.yml +0 -169
- package/.vscode/mcp.json +0 -9
- package/docs/overview/comparison.mdx +0 -82
- package/testdriver/lifecycle/prerun.yaml +0 -17
- /package/{testdriver/examples/desktop/lifecycle/prerun.yaml → .env.example} +0 -0
- /package/{testdriver → _testdriver}/acceptance/assert.yaml +0 -0
- /package/{testdriver → _testdriver}/acceptance/dashcam.yaml +0 -0
- /package/{testdriver → _testdriver}/acceptance/embed.yaml +0 -0
- /package/{testdriver → _testdriver}/acceptance/exec-js.yaml +0 -0
- /package/{testdriver → _testdriver}/acceptance/exec-output.yaml +0 -0
- /package/{testdriver → _testdriver}/acceptance/exec-shell.yaml +0 -0
- /package/{testdriver → _testdriver}/acceptance/focus-window.yaml +0 -0
- /package/{testdriver → _testdriver}/acceptance/hover-image.yaml +0 -0
- /package/{testdriver → _testdriver}/acceptance/hover-text-with-description.yaml +0 -0
- /package/{testdriver → _testdriver}/acceptance/hover-text.yaml +0 -0
- /package/{testdriver → _testdriver}/acceptance/if-else.yaml +0 -0
- /package/{testdriver → _testdriver}/acceptance/match-image.yaml +0 -0
- /package/{testdriver → _testdriver}/acceptance/press-keys.yaml +0 -0
- /package/{testdriver → _testdriver}/acceptance/prompt.yaml +0 -0
- /package/{testdriver → _testdriver}/acceptance/remember.yaml +0 -0
- /package/{testdriver → _testdriver}/acceptance/screenshots/cart.png +0 -0
- /package/{testdriver → _testdriver}/acceptance/scroll-keyboard.yaml +0 -0
- /package/{testdriver → _testdriver}/acceptance/scroll-until-image.yaml +0 -0
- /package/{testdriver → _testdriver}/acceptance/scroll-until-text.yaml +0 -0
- /package/{testdriver → _testdriver}/acceptance/scroll.yaml +0 -0
- /package/{testdriver → _testdriver}/acceptance/snippets/match-cart.yaml +0 -0
- /package/{testdriver → _testdriver}/acceptance/type.yaml +0 -0
- /package/{testdriver → _testdriver}/behavior/failure.yaml +0 -0
- /package/{testdriver → _testdriver}/behavior/hover-text.yaml +0 -0
- /package/{testdriver → _testdriver}/behavior/lifecycle/postrun.yaml +0 -0
- /package/{testdriver → _testdriver}/behavior/lifecycle/prerun.yaml +0 -0
- /package/{testdriver → _testdriver}/behavior/lifecycle/provision.yaml +0 -0
- /package/{testdriver → _testdriver}/behavior/secrets.yaml +0 -0
- /package/{testdriver → _testdriver}/edge-cases/dashcam-chrome.yaml +0 -0
- /package/{testdriver → _testdriver}/edge-cases/exec-pwsh-multiline.yaml +0 -0
- /package/{testdriver → _testdriver}/edge-cases/js-exception.yaml +0 -0
- /package/{testdriver → _testdriver}/edge-cases/js-promise.yaml +0 -0
- /package/{testdriver → _testdriver}/edge-cases/lifecycle/postrun.yaml +0 -0
- /package/{testdriver → _testdriver}/edge-cases/prompt-in-middle.yaml +0 -0
- /package/{testdriver → _testdriver}/edge-cases/prompt-nested.yaml +0 -0
- /package/{testdriver → _testdriver}/edge-cases/success-test.yaml +0 -0
- /package/{testdriver → _testdriver}/examples/android/example.yaml +0 -0
- /package/{testdriver → _testdriver}/examples/android/lifecycle/postrun.yaml +0 -0
- /package/{testdriver → _testdriver}/examples/android/lifecycle/provision.yaml +0 -0
- /package/{testdriver → _testdriver}/examples/android/readme.md +0 -0
- /package/{testdriver → _testdriver}/examples/chrome-extension/lifecycle/provision.yaml +0 -0
- /package/{testdriver → _testdriver}/examples/desktop/lifecycle/provision.yaml +0 -0
- /package/{testdriver → _testdriver}/examples/vscode-extension/lifecycle/provision.yaml +0 -0
- /package/{testdriver → _testdriver}/examples/web/lifecycle/postrun.yaml +0 -0
- /package/docs/{account → v6/account}/dashboard.mdx +0 -0
- /package/docs/{account → v6/account}/enterprise.mdx +0 -0
- /package/docs/{account → v6/account}/pricing.mdx +0 -0
- /package/docs/{account → v6/account}/projects.mdx +0 -0
- /package/docs/{account → v6/account}/team.mdx +0 -0
- /package/docs/{action → v6/action}/ami.mdx +0 -0
- /package/docs/{action → v6/action}/performance.mdx +0 -0
- /package/docs/{action → v6/action}/secrets.mdx +0 -0
- /package/docs/{apps → v6/apps}/chrome-extensions.mdx +0 -0
- /package/docs/{apps → v6/apps}/desktop-apps.mdx +0 -0
- /package/docs/{apps → v6/apps}/mobile-apps.mdx +0 -0
- /package/docs/{apps → v6/apps}/static-websites.mdx +0 -0
- /package/docs/{apps → v6/apps}/tauri-apps.mdx +0 -0
- /package/docs/{bugs → v6/bugs}/jira.mdx +0 -0
- /package/docs/{cli → v6/cli}/overview.mdx +0 -0
- /package/docs/{commands → v6/commands}/assert.mdx +0 -0
- /package/docs/{commands → v6/commands}/exec.mdx +0 -0
- /package/docs/{commands → v6/commands}/focus-application.mdx +0 -0
- /package/docs/{commands → v6/commands}/hover-image.mdx +0 -0
- /package/docs/{commands → v6/commands}/hover-text.mdx +0 -0
- /package/docs/{commands → v6/commands}/if.mdx +0 -0
- /package/docs/{commands → v6/commands}/match-image.mdx +0 -0
- /package/docs/{commands → v6/commands}/press-keys.mdx +0 -0
- /package/docs/{commands → v6/commands}/remember.mdx +0 -0
- /package/docs/{commands → v6/commands}/run.mdx +0 -0
- /package/docs/{commands → v6/commands}/scroll-until-image.mdx +0 -0
- /package/docs/{commands → v6/commands}/scroll-until-text.mdx +0 -0
- /package/docs/{commands → v6/commands}/scroll.mdx +0 -0
- /package/docs/{commands → v6/commands}/type.mdx +0 -0
- /package/docs/{commands → v6/commands}/wait-for-image.mdx +0 -0
- /package/docs/{commands → v6/commands}/wait-for-text.mdx +0 -0
- /package/docs/{commands → v6/commands}/wait.mdx +0 -0
- /package/docs/{exporting → v6/exporting}/junit.mdx +0 -0
- /package/docs/{exporting → v6/exporting}/playwright.mdx +0 -0
- /package/docs/{features → v6/features}/auto-healing.mdx +0 -0
- /package/docs/{features → v6/features}/generation.mdx +0 -0
- /package/docs/{features → v6/features}/parallel-testing.mdx +0 -0
- /package/docs/{features → v6/features}/reusable-snippets.mdx +0 -0
- /package/docs/{features → v6/features}/selectorless.mdx +0 -0
- /package/docs/{features → v6/features}/visual-assertions.mdx +0 -0
- /package/docs/{getting-started → v6/getting-started}/ci.mdx +0 -0
- /package/docs/{getting-started → v6/getting-started}/cli.mdx +0 -0
- /package/docs/{getting-started → v6/getting-started}/editing.mdx +0 -0
- /package/docs/{getting-started → v6/getting-started}/playwright.mdx +0 -0
- /package/docs/{getting-started → v6/getting-started}/running.mdx +0 -0
- /package/docs/{getting-started → v6/getting-started}/vscode.mdx +0 -0
- /package/docs/{guide → v6/guide}/assertions.mdx +0 -0
- /package/docs/{guide → v6/guide}/authentication.mdx +0 -0
- /package/docs/{guide → v6/guide}/code.mdx +0 -0
- /package/docs/{guide → v6/guide}/locating.mdx +0 -0
- /package/docs/{guide → v6/guide}/protips.mdx +0 -0
- /package/docs/{guide → v6/guide}/variables.mdx +0 -0
- /package/docs/{guide → v6/guide}/waiting.mdx +0 -0
- /package/docs/{importing → v6/importing}/csv.mdx +0 -0
- /package/docs/{importing → v6/importing}/gherkin.mdx +0 -0
- /package/docs/{importing → v6/importing}/jira.mdx +0 -0
- /package/docs/{importing → v6/importing}/testrail.mdx +0 -0
- /package/docs/{integrations → v6/integrations}/electron.mdx +0 -0
- /package/docs/{integrations → v6/integrations}/netlify.mdx +0 -0
- /package/docs/{integrations → v6/integrations}/vercel.mdx +0 -0
- /package/docs/{interactive → v6/interactive}/explore.mdx +0 -0
- /package/docs/{interactive → v6/interactive}/run.mdx +0 -0
- /package/docs/{interactive → v6/interactive}/save.mdx +0 -0
- /package/docs/{overview → v6/overview}/faq.mdx +0 -0
- /package/docs/{overview → v6/overview}/performance.mdx +0 -0
- /package/docs/{overview → v6/overview}/quickstart.mdx +0 -0
- /package/docs/{overview → v6/overview}/what-is-testdriver.mdx +0 -0
- /package/docs/{scenarios → v6/scenarios}/ai-chatbot.mdx +0 -0
- /package/docs/{scenarios → v6/scenarios}/cookie-banner.mdx +0 -0
- /package/docs/{scenarios → v6/scenarios}/file-upload.mdx +0 -0
- /package/docs/{scenarios → v6/scenarios}/form-filling.mdx +0 -0
- /package/docs/{scenarios → v6/scenarios}/log-in.mdx +0 -0
- /package/docs/{scenarios → v6/scenarios}/pdf-generation.mdx +0 -0
- /package/docs/{scenarios → v6/scenarios}/spell-check.mdx +0 -0
- /package/docs/{security → v6/security}/action.mdx +0 -0
- /package/docs/{security → v6/security}/agent.mdx +0 -0
- /package/docs/{security → v6/security}/platform.mdx +0 -0
- /package/docs/{tutorials → v6/tutorials}/advanced-test.mdx +0 -0
- /package/docs/{tutorials → v6/tutorials}/basic-test.mdx +0 -0
package/agent/lib/redraw.js
CHANGED
|
@@ -3,9 +3,28 @@ const fs = require("fs");
|
|
|
3
3
|
const { events } = require("../events");
|
|
4
4
|
const theme = require("./theme");
|
|
5
5
|
|
|
6
|
+
// Default redraw options
|
|
7
|
+
const DEFAULT_REDRAW_OPTIONS = {
|
|
8
|
+
enabled: true, // Master switch to enable/disable redraw detection
|
|
9
|
+
screenRedraw: true, // Enable screen redraw detection
|
|
10
|
+
networkMonitor: true, // Enable network activity monitoring
|
|
11
|
+
diffThreshold: 0.1, // Percentage threshold for screen diff (0.1 = 0.1%)
|
|
12
|
+
};
|
|
13
|
+
|
|
6
14
|
// Factory function that creates redraw functionality with the provided system instance
|
|
7
|
-
const createRedraw = (
|
|
8
|
-
|
|
15
|
+
const createRedraw = (
|
|
16
|
+
emitter,
|
|
17
|
+
system,
|
|
18
|
+
sandbox,
|
|
19
|
+
defaultOptions = {},
|
|
20
|
+
) => {
|
|
21
|
+
// Merge default options with provided defaults
|
|
22
|
+
const baseOptions = { ...DEFAULT_REDRAW_OPTIONS, ...defaultOptions };
|
|
23
|
+
// Support legacy redrawThresholdPercent number argument
|
|
24
|
+
if (typeof defaultOptions === 'number') {
|
|
25
|
+
baseOptions.diffThreshold = defaultOptions;
|
|
26
|
+
}
|
|
27
|
+
|
|
9
28
|
const networkUpdateInterval = 15000;
|
|
10
29
|
|
|
11
30
|
let lastTxBytes = null;
|
|
@@ -69,7 +88,6 @@ const createRedraw = (emitter, system, sandbox) => {
|
|
|
69
88
|
async function updateNetwork() {
|
|
70
89
|
if (sandbox && sandbox.instanceSocketConnected) {
|
|
71
90
|
let network = await sandbox.send({
|
|
72
|
-
os: "linux",
|
|
73
91
|
type: "system.network",
|
|
74
92
|
});
|
|
75
93
|
parseNetworkStats(
|
|
@@ -113,16 +131,13 @@ const createRedraw = (emitter, system, sandbox) => {
|
|
|
113
131
|
{ threshold: 0.1 },
|
|
114
132
|
);
|
|
115
133
|
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
const diffPercentage = (differentPixels / totalPixels) * 100;
|
|
121
|
-
return diffPercentage.toFixed(1);
|
|
122
|
-
}
|
|
134
|
+
// Calculate percentage difference based on pixel differences
|
|
135
|
+
// Always return a number (0 if no difference)
|
|
136
|
+
const diffPercentage = (differentPixels / totalPixels) * 100;
|
|
137
|
+
return parseFloat(diffPercentage.toFixed(2));
|
|
123
138
|
} catch (error) {
|
|
124
139
|
console.error("Error comparing images:", error);
|
|
125
|
-
return false
|
|
140
|
+
return 0; // Return 0 on error instead of false
|
|
126
141
|
}
|
|
127
142
|
}
|
|
128
143
|
|
|
@@ -143,46 +158,104 @@ const createRedraw = (emitter, system, sandbox) => {
|
|
|
143
158
|
}
|
|
144
159
|
}
|
|
145
160
|
|
|
146
|
-
|
|
161
|
+
// Current options for the active redraw cycle
|
|
162
|
+
let currentOptions = { ...baseOptions };
|
|
163
|
+
|
|
164
|
+
async function start(options = {}) {
|
|
165
|
+
// Merge base options with per-call options
|
|
166
|
+
currentOptions = { ...baseOptions, ...options };
|
|
167
|
+
|
|
168
|
+
console.log('[redraw] start() called with options:', JSON.stringify(currentOptions));
|
|
169
|
+
|
|
170
|
+
// If redraw is completely disabled, return early
|
|
171
|
+
if (!currentOptions.enabled) {
|
|
172
|
+
console.log('[redraw] start() - redraw disabled, returning null');
|
|
173
|
+
return null;
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
// If both screenRedraw and networkMonitor are disabled, disable redraw
|
|
177
|
+
if (!currentOptions.screenRedraw && !currentOptions.networkMonitor) {
|
|
178
|
+
currentOptions.enabled = false;
|
|
179
|
+
console.log('[redraw] start() - both screenRedraw and networkMonitor disabled, returning null');
|
|
180
|
+
return null;
|
|
181
|
+
}
|
|
182
|
+
|
|
147
183
|
resetState();
|
|
148
|
-
|
|
149
|
-
|
|
184
|
+
|
|
185
|
+
// Only start network monitoring if enabled
|
|
186
|
+
if (currentOptions.networkMonitor) {
|
|
187
|
+
startNetworkMonitoring();
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
// Only capture start image if screen redraw is enabled
|
|
191
|
+
if (currentOptions.screenRedraw) {
|
|
192
|
+
startImage = await system.captureScreenPNG(0.25, true);
|
|
193
|
+
console.log('[redraw] start() - captured startImage:', startImage);
|
|
194
|
+
}
|
|
195
|
+
|
|
150
196
|
return startImage;
|
|
151
197
|
}
|
|
152
198
|
|
|
153
|
-
async function checkCondition(resolve, startTime, timeoutMs) {
|
|
154
|
-
|
|
199
|
+
async function checkCondition(resolve, startTime, timeoutMs, options) {
|
|
200
|
+
const { enabled, screenRedraw, networkMonitor, diffThreshold } = options;
|
|
201
|
+
|
|
202
|
+
// If redraw is disabled, resolve immediately
|
|
203
|
+
if (!enabled) {
|
|
204
|
+
resolve("true");
|
|
205
|
+
return;
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
let nowImage = screenRedraw ? await system.captureScreenPNG(0.25, true) : null;
|
|
155
209
|
let timeElapsed = Date.now() - startTime;
|
|
156
210
|
let diffPercent = 0;
|
|
157
211
|
let isTimeout = timeElapsed > timeoutMs;
|
|
158
212
|
|
|
159
|
-
if
|
|
213
|
+
// Check screen redraw if enabled and we have a start image to compare against
|
|
214
|
+
if (screenRedraw && !screenHasRedrawn && startImage && nowImage) {
|
|
215
|
+
console.log('[redraw] checkCondition() - comparing images:', { startImage, nowImage });
|
|
160
216
|
diffPercent = await imageDiffPercent(startImage, nowImage);
|
|
161
|
-
|
|
217
|
+
console.log('[redraw] checkCondition() - diffPercent:', diffPercent, 'threshold:', diffThreshold);
|
|
218
|
+
screenHasRedrawn = diffPercent > diffThreshold;
|
|
219
|
+
console.log('[redraw] checkCondition() - screenHasRedrawn:', screenHasRedrawn);
|
|
220
|
+
} else if (screenRedraw && !startImage) {
|
|
221
|
+
// If no start image was captured, capture one now and wait for next check
|
|
222
|
+
console.log('[redraw] checkCondition() - no startImage, capturing now');
|
|
223
|
+
startImage = await system.captureScreenPNG(0.25, true);
|
|
162
224
|
}
|
|
163
|
-
|
|
164
|
-
//
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
225
|
+
|
|
226
|
+
// If screen redraw is disabled, consider it as "redrawn"
|
|
227
|
+
const effectiveScreenRedrawn = screenRedraw ? screenHasRedrawn : true;
|
|
228
|
+
// If network monitor is disabled, consider it as "settled"
|
|
229
|
+
const effectiveNetworkSettled = networkMonitor ? networkSettled : true;
|
|
230
|
+
|
|
231
|
+
// Log redraw status
|
|
232
|
+
let redrawText = !screenRedraw
|
|
233
|
+
? theme.dim(`disabled`)
|
|
234
|
+
: effectiveScreenRedrawn
|
|
235
|
+
? theme.green(`y`)
|
|
236
|
+
: theme.dim(`${diffPercent}/${diffThreshold}%`);
|
|
237
|
+
let networkText = !networkMonitor
|
|
238
|
+
? theme.dim(`disabled`)
|
|
239
|
+
: effectiveNetworkSettled
|
|
240
|
+
? theme.green(`y`)
|
|
241
|
+
: theme.dim(
|
|
242
|
+
`${Math.trunc((diffRxBytes + diffTxBytes) / networkUpdateInterval)}b/s`,
|
|
243
|
+
);
|
|
173
244
|
let timeoutText = isTimeout
|
|
174
245
|
? theme.green(`y`)
|
|
175
246
|
: theme.dim(`${Math.floor(timeElapsed / 1000)}/${timeoutMs / 1000}s`);
|
|
176
247
|
|
|
177
248
|
emitter.emit(events.redraw.status, {
|
|
178
249
|
redraw: {
|
|
179
|
-
|
|
250
|
+
enabled: screenRedraw,
|
|
251
|
+
hasRedrawn: effectiveScreenRedrawn,
|
|
180
252
|
diffPercent,
|
|
181
|
-
threshold:
|
|
253
|
+
threshold: diffThreshold,
|
|
182
254
|
text: redrawText,
|
|
183
255
|
},
|
|
184
256
|
network: {
|
|
185
|
-
|
|
257
|
+
enabled: networkMonitor,
|
|
258
|
+
settled: effectiveNetworkSettled,
|
|
186
259
|
rxBytes: diffRxBytes,
|
|
187
260
|
txBytes: diffTxBytes,
|
|
188
261
|
text: networkText,
|
|
@@ -195,27 +268,42 @@ const createRedraw = (emitter, system, sandbox) => {
|
|
|
195
268
|
},
|
|
196
269
|
});
|
|
197
270
|
|
|
198
|
-
if ((
|
|
271
|
+
if ((effectiveScreenRedrawn && effectiveNetworkSettled) || isTimeout) {
|
|
199
272
|
emitter.emit(events.redraw.complete, {
|
|
200
|
-
screenHasRedrawn,
|
|
201
|
-
networkSettled,
|
|
273
|
+
screenHasRedrawn: effectiveScreenRedrawn,
|
|
274
|
+
networkSettled: effectiveNetworkSettled,
|
|
202
275
|
isTimeout,
|
|
203
276
|
timeElapsed,
|
|
204
277
|
});
|
|
205
278
|
resolve("true");
|
|
206
279
|
} else {
|
|
207
280
|
setTimeout(() => {
|
|
208
|
-
checkCondition(resolve, startTime, timeoutMs);
|
|
281
|
+
checkCondition(resolve, startTime, timeoutMs, options);
|
|
209
282
|
}, 500);
|
|
210
283
|
}
|
|
211
284
|
}
|
|
212
285
|
|
|
213
|
-
function wait(timeoutMs) {
|
|
286
|
+
function wait(timeoutMs, options = {}) {
|
|
287
|
+
// Merge current options with any per-call overrides
|
|
288
|
+
const waitOptions = { ...currentOptions, ...options };
|
|
289
|
+
|
|
290
|
+
// If redraw is disabled, resolve immediately
|
|
291
|
+
if (!waitOptions.enabled) {
|
|
292
|
+
return Promise.resolve("true");
|
|
293
|
+
}
|
|
294
|
+
|
|
295
|
+
// If both are disabled, resolve immediately
|
|
296
|
+
if (!waitOptions.screenRedraw && !waitOptions.networkMonitor) {
|
|
297
|
+
return Promise.resolve("true");
|
|
298
|
+
}
|
|
299
|
+
|
|
214
300
|
return new Promise((resolve) => {
|
|
215
301
|
const startTime = Date.now();
|
|
216
|
-
// Start network monitoring if not already started
|
|
217
|
-
|
|
218
|
-
|
|
302
|
+
// Start network monitoring if not already started and enabled
|
|
303
|
+
if (waitOptions.networkMonitor) {
|
|
304
|
+
startNetworkMonitoring();
|
|
305
|
+
}
|
|
306
|
+
checkCondition(resolve, startTime, timeoutMs, waitOptions);
|
|
219
307
|
});
|
|
220
308
|
}
|
|
221
309
|
|
|
@@ -223,7 +311,7 @@ const createRedraw = (emitter, system, sandbox) => {
|
|
|
223
311
|
stopNetworkMonitoring(networkInterval);
|
|
224
312
|
}
|
|
225
313
|
|
|
226
|
-
return { start, wait, cleanup };
|
|
314
|
+
return { start, wait, cleanup, DEFAULT_OPTIONS: DEFAULT_REDRAW_OPTIONS };
|
|
227
315
|
};
|
|
228
316
|
|
|
229
|
-
module.exports = { createRedraw };
|
|
317
|
+
module.exports = { createRedraw, DEFAULT_REDRAW_OPTIONS };
|
package/agent/lib/sandbox.js
CHANGED
|
@@ -2,7 +2,7 @@ const WebSocket = require("ws");
|
|
|
2
2
|
const marky = require("marky");
|
|
3
3
|
const { events } = require("../events");
|
|
4
4
|
|
|
5
|
-
const createSandbox = (emitter, analytics) => {
|
|
5
|
+
const createSandbox = (emitter, analytics, sessionInstance) => {
|
|
6
6
|
class Sandbox {
|
|
7
7
|
constructor() {
|
|
8
8
|
this.socket = null;
|
|
@@ -14,6 +14,8 @@ const createSandbox = (emitter, analytics) => {
|
|
|
14
14
|
this.instance = null;
|
|
15
15
|
this.messageId = 0;
|
|
16
16
|
this.uniqueId = Math.random().toString(36).substring(7);
|
|
17
|
+
this.os = null; // Store OS value to send with every message
|
|
18
|
+
this.sessionInstance = sessionInstance; // Store session instance to include in messages
|
|
17
19
|
}
|
|
18
20
|
|
|
19
21
|
send(message) {
|
|
@@ -24,6 +26,24 @@ const createSandbox = (emitter, analytics) => {
|
|
|
24
26
|
this.messageId++;
|
|
25
27
|
message.requestId = `${this.uniqueId}-${this.messageId}`;
|
|
26
28
|
|
|
29
|
+
// If os is set in the message, store it for future messages
|
|
30
|
+
if (message.os) {
|
|
31
|
+
this.os = message.os;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
// Add os to every message if it's been set
|
|
35
|
+
if (this.os && !message.os) {
|
|
36
|
+
message.os = this.os;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
// Add session to every message if available (for interaction tracking)
|
|
40
|
+
if (this.sessionInstance && !message.session) {
|
|
41
|
+
const sessionId = this.sessionInstance.get();
|
|
42
|
+
if (sessionId) {
|
|
43
|
+
message.session = sessionId;
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
|
|
27
47
|
// Start timing for this message
|
|
28
48
|
const timingKey = `sandbox-${message.type}`;
|
|
29
49
|
marky.mark(timingKey);
|
|
@@ -51,7 +71,6 @@ const createSandbox = (emitter, analytics) => {
|
|
|
51
71
|
async auth(apiKey) {
|
|
52
72
|
let reply = await this.send({
|
|
53
73
|
type: "authenticate",
|
|
54
|
-
os: "linux",
|
|
55
74
|
apiKey,
|
|
56
75
|
});
|
|
57
76
|
|
|
@@ -65,7 +84,6 @@ const createSandbox = (emitter, analytics) => {
|
|
|
65
84
|
async connect(sandboxId, persist = false) {
|
|
66
85
|
let reply = await this.send({
|
|
67
86
|
type: "connect",
|
|
68
|
-
os: "linux",
|
|
69
87
|
persist,
|
|
70
88
|
sandboxId,
|
|
71
89
|
});
|
|
@@ -73,9 +91,12 @@ const createSandbox = (emitter, analytics) => {
|
|
|
73
91
|
if (reply.success) {
|
|
74
92
|
this.instanceSocketConnected = true;
|
|
75
93
|
emitter.emit(events.sandbox.connected);
|
|
94
|
+
// Return the full reply (includes url and sandbox)
|
|
95
|
+
return reply;
|
|
96
|
+
} else {
|
|
97
|
+
// Throw error to trigger fallback to creating new sandbox
|
|
98
|
+
throw new Error(reply.errorMessage || "Failed to connect to sandbox");
|
|
76
99
|
}
|
|
77
|
-
|
|
78
|
-
return reply.sandbox;
|
|
79
100
|
}
|
|
80
101
|
|
|
81
102
|
async boot(apiRoot) {
|
|
@@ -124,7 +145,9 @@ const createSandbox = (emitter, analytics) => {
|
|
|
124
145
|
|
|
125
146
|
if (message.error) {
|
|
126
147
|
emitter.emit(events.error.sandbox, message.errorMessage);
|
|
127
|
-
|
|
148
|
+
const error = new Error(message.errorMessage || "Sandbox error");
|
|
149
|
+
error.responseData = message;
|
|
150
|
+
this.ps[message.requestId].reject(error);
|
|
128
151
|
} else {
|
|
129
152
|
emitter.emit(events.sandbox.received);
|
|
130
153
|
|
package/agent/lib/sdk.js
CHANGED
|
@@ -123,6 +123,7 @@ const createSDK = (emitter, config, sessionInstance) => {
|
|
|
123
123
|
...(token && { Authorization: `Bearer ${token}` }), // Add the authorization bearer token only if token is set
|
|
124
124
|
},
|
|
125
125
|
responseType: typeof onChunk === "function" ? "stream" : "json",
|
|
126
|
+
timeout: 60000, // 60 second timeout to prevent hanging requests
|
|
126
127
|
data: {
|
|
127
128
|
...data,
|
|
128
129
|
session: sessionInstance.get(),
|
|
@@ -193,6 +194,27 @@ const createSDK = (emitter, config, sessionInstance) => {
|
|
|
193
194
|
|
|
194
195
|
return value;
|
|
195
196
|
} catch (error) {
|
|
197
|
+
// Check if this is an API validation error with detailed problems
|
|
198
|
+
if (error.response?.data?.problems) {
|
|
199
|
+
const problems = error.response.data.problems;
|
|
200
|
+
const errorMessage = error.response.data.message || 'API validation error';
|
|
201
|
+
const detailedError = new Error(
|
|
202
|
+
`${errorMessage}\n\nDetails:\n${problems.map(p => ` - ${p}`).join('\n')}`
|
|
203
|
+
);
|
|
204
|
+
detailedError.originalError = error;
|
|
205
|
+
detailedError.problems = problems;
|
|
206
|
+
|
|
207
|
+
// Emit the formatted error
|
|
208
|
+
emitter.emit(events.error.sdk, {
|
|
209
|
+
message: detailedError.message,
|
|
210
|
+
code: error.response?.data?.code || error.code,
|
|
211
|
+
problems: problems,
|
|
212
|
+
fullError: error,
|
|
213
|
+
});
|
|
214
|
+
|
|
215
|
+
throw detailedError;
|
|
216
|
+
}
|
|
217
|
+
|
|
196
218
|
outputError(error);
|
|
197
219
|
throw error; // Re-throw the error so calling code can handle it properly
|
|
198
220
|
}
|
package/agent/lib/system.js
CHANGED
|
@@ -8,7 +8,6 @@ const { events } = require("../events.js");
|
|
|
8
8
|
const createSystem = (emitter, sandbox, config) => {
|
|
9
9
|
const screenshot = async (options) => {
|
|
10
10
|
let { base64 } = await sandbox.send({
|
|
11
|
-
os: "linux",
|
|
12
11
|
type: "system.screenshot",
|
|
13
12
|
});
|
|
14
13
|
|
|
@@ -113,7 +112,6 @@ const createSystem = (emitter, sandbox, config) => {
|
|
|
113
112
|
const activeWin = async () => {
|
|
114
113
|
// Get Mouse Position from command line
|
|
115
114
|
let result = await sandbox.send({
|
|
116
|
-
os: "linux",
|
|
117
115
|
type: "system.get-active-window",
|
|
118
116
|
});
|
|
119
117
|
|
|
@@ -123,7 +121,6 @@ const createSystem = (emitter, sandbox, config) => {
|
|
|
123
121
|
const getMousePosition = async () => {
|
|
124
122
|
// Get Mouse Position from command line
|
|
125
123
|
let result = await sandbox.send({
|
|
126
|
-
os: "linux",
|
|
127
124
|
type: "system.get-mouse-position",
|
|
128
125
|
});
|
|
129
126
|
|
package/agent/lib/validation.js
CHANGED
|
@@ -62,33 +62,27 @@ const types = () =>
|
|
|
62
62
|
},
|
|
63
63
|
// - command: scroll # Scroll up or down. Make sure the correct portion of the page is focused before scrolling.
|
|
64
64
|
// direction: down # Available directions are: up, down, left, right
|
|
65
|
-
//
|
|
66
|
-
// amount: 300 # Optional. The amount of pixels to scroll. Defaults to 300 for keyboard and 200 for mouse.
|
|
65
|
+
// amount: 300 # Optional. The amount of pixels to scroll. Defaults to 300.
|
|
67
66
|
ScrollCommand: {
|
|
68
67
|
command: '"scroll"',
|
|
69
68
|
direction: '"up" | "down" | "left" | "right"',
|
|
70
|
-
"method?": '"keyboard" | "mouse"',
|
|
71
69
|
"amount?": "number",
|
|
72
70
|
},
|
|
73
71
|
// - command: scroll-until-text # Scroll until text is found
|
|
74
72
|
// text: Sign Up # The text to find on screen. The longer and more unique the better.
|
|
75
73
|
// direction: down # Available directions are: up, down, left, right
|
|
76
|
-
// method: keyboard # Optional. Available methods are: keyboard (default), mouse. Use mouse only if the prompt explicitly asks for it.
|
|
77
74
|
ScrollUntilTextCommand: {
|
|
78
75
|
command: '"scroll-until-text"',
|
|
79
76
|
text: "string",
|
|
80
77
|
direction: '"up" | "down" | "left" | "right"',
|
|
81
|
-
"method?": '"keyboard" | "mouse"',
|
|
82
78
|
},
|
|
83
79
|
// - command: scroll-until-image # Scroll until icon or image is found
|
|
84
80
|
// description: Submit at the bottom of the form
|
|
85
81
|
// direction: down # Available directions are: up, down, left, rights
|
|
86
|
-
// method: keyboard # Optional. Available methods are: keyboard (default), mouse. Use mouse only if the prompt explicitly asks for it.
|
|
87
82
|
ScrollUntilImageCommand: {
|
|
88
83
|
command: '"scroll-until-image"',
|
|
89
84
|
description: "string",
|
|
90
85
|
direction: '"up" | "down" | "left" | "right"',
|
|
91
|
-
"method?": '"keyboard" | "mouse"',
|
|
92
86
|
},
|
|
93
87
|
// - command: wait-for-text # Wait until text is seen on screen. Not recommended unless explicitly requested by user.
|
|
94
88
|
// text: Copyright 2024 # The text to find on screen.
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Debug script to inspect the full locate API response
|
|
5
|
+
* Run this with: TD_API_KEY=your_key node debug-locate-response.js
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
const TestDriverSDK = require("./sdk.js");
|
|
9
|
+
|
|
10
|
+
async function debugLocateResponse() {
|
|
11
|
+
const client = new TestDriverSDK(process.env.TD_API_KEY);
|
|
12
|
+
|
|
13
|
+
try {
|
|
14
|
+
console.log("Connecting to sandbox (Linux)...");
|
|
15
|
+
await client.connect({ headless: true });
|
|
16
|
+
|
|
17
|
+
console.log("Opening a test page...");
|
|
18
|
+
await client.focusApplication("Google Chrome");
|
|
19
|
+
await client.type("https://example.com");
|
|
20
|
+
await client.pressKeys(["enter"]);
|
|
21
|
+
|
|
22
|
+
// Wait for page to load
|
|
23
|
+
await new Promise((resolve) => setTimeout(resolve, 3000));
|
|
24
|
+
|
|
25
|
+
console.log("\nFinding an element to inspect the response...");
|
|
26
|
+
const element = await client.find("the heading that says Example Domain");
|
|
27
|
+
|
|
28
|
+
console.log("\n=".repeat(60));
|
|
29
|
+
console.log("FULL LOCATE API RESPONSE:");
|
|
30
|
+
console.log("=".repeat(60));
|
|
31
|
+
|
|
32
|
+
const response = element.getResponse();
|
|
33
|
+
console.log(JSON.stringify(response, null, 2));
|
|
34
|
+
|
|
35
|
+
console.log("\n=".repeat(60));
|
|
36
|
+
console.log("RESPONSE KEYS:");
|
|
37
|
+
console.log("=".repeat(60));
|
|
38
|
+
|
|
39
|
+
if (response) {
|
|
40
|
+
Object.keys(response).forEach((key) => {
|
|
41
|
+
const value = response[key];
|
|
42
|
+
const type = Array.isArray(value) ? "array" : typeof value;
|
|
43
|
+
const preview =
|
|
44
|
+
typeof value === "string" && value.length > 100
|
|
45
|
+
? `${value.substring(0, 100)}... (${value.length} chars)`
|
|
46
|
+
: typeof value === "object"
|
|
47
|
+
? JSON.stringify(value)
|
|
48
|
+
: value;
|
|
49
|
+
|
|
50
|
+
console.log(` ${key} (${type}): ${preview}`);
|
|
51
|
+
});
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
console.log("\n=".repeat(60));
|
|
55
|
+
console.log("ELEMENT PROPERTIES:");
|
|
56
|
+
console.log("=".repeat(60));
|
|
57
|
+
console.log(" found:", element.found());
|
|
58
|
+
console.log(" x:", element.x);
|
|
59
|
+
console.log(" y:", element.y);
|
|
60
|
+
console.log(" centerX:", element.centerX);
|
|
61
|
+
console.log(" centerY:", element.centerY);
|
|
62
|
+
console.log(" width:", element.width);
|
|
63
|
+
console.log(" height:", element.height);
|
|
64
|
+
console.log(" confidence:", element.confidence);
|
|
65
|
+
console.log(" text:", element.text);
|
|
66
|
+
console.log(" label:", element.label);
|
|
67
|
+
console.log(
|
|
68
|
+
" screenshot:",
|
|
69
|
+
element.screenshot ? `${element.screenshot.length} chars` : null,
|
|
70
|
+
);
|
|
71
|
+
console.log(" boundingBox:", element.boundingBox);
|
|
72
|
+
|
|
73
|
+
await client.disconnect();
|
|
74
|
+
} catch (error) {
|
|
75
|
+
console.error("Error:", error.message);
|
|
76
|
+
console.error(error.stack);
|
|
77
|
+
await client.disconnect();
|
|
78
|
+
process.exit(1);
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
debugLocateResponse();
|
package/debugger/index.html
CHANGED
|
@@ -34,10 +34,14 @@
|
|
|
34
34
|
top: 0;
|
|
35
35
|
left: 0;
|
|
36
36
|
transform-origin: top left;
|
|
37
|
+
overflow: hidden;
|
|
37
38
|
}
|
|
38
39
|
|
|
40
|
+
html,
|
|
39
41
|
body {
|
|
40
42
|
overflow: hidden;
|
|
43
|
+
width: 100%;
|
|
44
|
+
height: 100%;
|
|
41
45
|
}
|
|
42
46
|
|
|
43
47
|
*::-webkit-scrollbar {
|
|
@@ -66,6 +70,7 @@
|
|
|
66
70
|
|
|
67
71
|
.overlay {
|
|
68
72
|
position: relative;
|
|
73
|
+
overflow: hidden;
|
|
69
74
|
}
|
|
70
75
|
|
|
71
76
|
.screenshot {
|
|
@@ -179,6 +184,7 @@
|
|
|
179
184
|
width: 100%;
|
|
180
185
|
height: 100%;
|
|
181
186
|
z-index: 1;
|
|
187
|
+
overflow: hidden;
|
|
182
188
|
}
|
|
183
189
|
|
|
184
190
|
/* Loading screen styles */
|
|
@@ -354,6 +360,10 @@
|
|
|
354
360
|
|
|
355
361
|
const iframe = document.querySelector("#vm-iframe");
|
|
356
362
|
|
|
363
|
+
// Detect if OS is Linux
|
|
364
|
+
const isLinux = parsedData.os === "linux";
|
|
365
|
+
const topBarOffset = isLinux ? 14 : 0;
|
|
366
|
+
|
|
357
367
|
// set overlay width and height to match the given resolution
|
|
358
368
|
const overlayWidth = parsedData.resolution[0];
|
|
359
369
|
const overlayHeight = parsedData.resolution[1];
|
|
@@ -361,7 +371,8 @@
|
|
|
361
371
|
iframe.style.display = "block";
|
|
362
372
|
iframe.src = parsedData.url;
|
|
363
373
|
iframe.style.width = overlayWidth + "px";
|
|
364
|
-
iframe
|
|
374
|
+
// Increase iframe height by 14px for Linux to account for top bar
|
|
375
|
+
iframe.style.height = overlayHeight + topBarOffset + "px";
|
|
365
376
|
|
|
366
377
|
// Calculate scale factor to fit within window if needed
|
|
367
378
|
const windowWidth = window.innerWidth;
|
|
@@ -513,7 +524,7 @@
|
|
|
513
524
|
const boxElement = document.createElement("div");
|
|
514
525
|
boxElement.className = "bounding-box";
|
|
515
526
|
boxElement.style.left = toCss(box.x);
|
|
516
|
-
boxElement.style.top = toCss(box.y);
|
|
527
|
+
boxElement.style.top = toCss(box.y + topBarOffset);
|
|
517
528
|
boxElement.style.width = toCss(box.width);
|
|
518
529
|
boxElement.style.height = toCss(box.height);
|
|
519
530
|
effects.appendChild(boxElement);
|
|
@@ -541,14 +552,14 @@
|
|
|
541
552
|
// Mouse event handlers
|
|
542
553
|
addEventHandler(events.mouseMove, (event, { x, y } = {}) => {
|
|
543
554
|
mouse.style.marginLeft = toCss(x);
|
|
544
|
-
mouse.style.marginTop = toCss(y);
|
|
555
|
+
mouse.style.marginTop = toCss(y + topBarOffset);
|
|
545
556
|
});
|
|
546
557
|
|
|
547
558
|
addEventHandler(
|
|
548
559
|
events.mouseClick,
|
|
549
560
|
(event, { x, y, click = "single" } = {}) => {
|
|
550
561
|
mouse.style.marginLeft = toCss(x);
|
|
551
|
-
mouse.style.marginTop = toCss(y);
|
|
562
|
+
mouse.style.marginTop = toCss(y + topBarOffset);
|
|
552
563
|
// Reset class so animation can restart
|
|
553
564
|
mouse.setAttribute("class", "mouse");
|
|
554
565
|
// Force reflow
|