forked from google-gemini/gemini-cli
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathautomationOverlay.ts
More file actions
133 lines (122 loc) · 4.12 KB
/
Copy pathautomationOverlay.ts
File metadata and controls
133 lines (122 loc) · 4.12 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
/**
* @license
* Copyright 2026 Google LLC
* SPDX-License-Identifier: Apache-2.0
*/
/**
* @fileoverview Automation overlay utilities for visual indication during browser automation.
*
* Provides functions to inject and remove a pulsating blue border overlay
* that indicates when the browser is under AI agent control.
*
* Uses the Web Animations API instead of injected <style> tags so the
* animation works on sites with strict Content Security Policies (e.g. google.com).
*
* The script strings are passed to chrome-devtools-mcp's evaluate_script tool
* which expects a plain function expression (NOT an IIFE).
*/
import type { BrowserManager } from './browserManager.js';
import { debugLogger } from '../../utils/debugLogger.js';
const OVERLAY_ELEMENT_ID = '__gemini_automation_overlay';
/**
* Builds the JavaScript function string that injects the automation overlay.
*
* Returns a plain arrow-function expression (no trailing invocation) because
* chrome-devtools-mcp's evaluate_script tool invokes it internally.
*
* Avoids nested template literals by using string concatenation for cssText.
*/
function buildInjectionScript(): string {
// Build the script as a plain string – no nested template literals.
return [
'() => {',
` const id = '${OVERLAY_ELEMENT_ID}';`,
' const existing = document.getElementById(id);',
' if (existing) existing.remove();',
'',
' const overlay = document.createElement("div");',
' overlay.id = id;',
' overlay.setAttribute("aria-hidden", "true");',
' overlay.setAttribute("role", "presentation");',
'',
' overlay.style.position = "fixed";',
' overlay.style.top = "0";',
' overlay.style.left = "0";',
' overlay.style.right = "0";',
' overlay.style.bottom = "0";',
' overlay.style.zIndex = "2147483647";',
' overlay.style.pointerEvents = "none";',
' overlay.style.border = "6px solid rgba(66, 133, 244, 1.0)";',
'',
' document.documentElement.appendChild(overlay);',
'',
' try {',
' overlay.animate([',
' { borderColor: "rgba(66,133,244,0.3)", boxShadow: "inset 0 0 8px rgba(66,133,244,0.15)" },',
' { borderColor: "rgba(66,133,244,1.0)", boxShadow: "inset 0 0 16px rgba(66,133,244,0.5)" },',
' { borderColor: "rgba(66,133,244,0.3)", boxShadow: "inset 0 0 8px rgba(66,133,244,0.15)" }',
' ], { duration: 2000, iterations: Infinity, easing: "ease-in-out" });',
' } catch (e) {}',
'',
' return "overlay-injected";',
'}',
].join('\n');
}
/**
* Builds the JavaScript function string that removes the automation overlay.
*/
function buildRemovalScript(): string {
return [
'() => {',
` var el = document.getElementById('${OVERLAY_ELEMENT_ID}');`,
' if (el) el.remove();',
' return "overlay-removed";',
'}',
].join('\n');
}
/**
* Injects the automation overlay into the current page.
*/
export async function injectAutomationOverlay(
browserManager: BrowserManager,
signal?: AbortSignal,
): Promise<void> {
try {
debugLogger.log('Injecting automation overlay...');
const result = await browserManager.callTool(
'evaluate_script',
{ function: buildInjectionScript() },
signal,
);
if (result.isError) {
debugLogger.warn('Failed to inject automation overlay:', result);
} else {
debugLogger.log('Automation overlay injected successfully');
}
} catch (error) {
debugLogger.warn('Error injecting automation overlay:', error);
}
}
/**
* Removes the automation overlay from the current page.
*/
export async function removeAutomationOverlay(
browserManager: BrowserManager,
signal?: AbortSignal,
): Promise<void> {
try {
debugLogger.log('Removing automation overlay...');
const result = await browserManager.callTool(
'evaluate_script',
{ function: buildRemovalScript() },
signal,
);
if (result.isError) {
debugLogger.warn('Failed to remove automation overlay:', result);
} else {
debugLogger.log('Automation overlay removed successfully');
}
} catch (error) {
debugLogger.warn('Error removing automation overlay:', error);
}
}