Skip to content

Commit 1328305

Browse files
2 parents 7399604 + 6bd44d6 commit 1328305

11 files changed

Lines changed: 909 additions & 73 deletions

File tree

.gitignore

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -63,4 +63,7 @@ Apps/Windows/inspector.exe
6363
Apps/Windows/Element.xml
6464
Framework/settings.conf.lock
6565
Framework/Built_In_Automation/Desktop/Linux/latest_app.txt
66-
**/linux_screen.png
66+
**/linux_screen.png
67+
**/ios_screen.png
68+
**/ios_ui.xml
69+
**/ui.xml

Apps/Web/aiplugin/background.js

Lines changed: 23 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -135,6 +135,13 @@ if (navigator.userAgentData.platform.toLowerCase().includes('mac')) {
135135
}
136136
browserAppData.runtime.onMessage.addListener(
137137
function (request, sender, sendResponse) {
138+
139+
if (request.action === 'toggle_from_content_script') {
140+
// allows the floating button to trigger the toggle logic
141+
toggle(sender.tab);
142+
return;
143+
}
144+
138145
if (request.apiName == 'ai_record_single_action') {
139146
var url = `${zeuz_url}/ai_record_single_action/`
140147
fetch(url, {
@@ -181,4 +188,19 @@ browserAppData.runtime.onMessage.addListener(
181188
.then(text => { console.log(text); sendResponse(text); })
182189
}
183190
}
184-
);
191+
);
192+
193+
// add AI Inspector to the right click menu
194+
browserAppData.runtime.onInstalled.addListener(() => {
195+
browserAppData.contextMenus.create({
196+
id: "toggle-ai-inspect",
197+
title: "Inspect with AI",
198+
contexts: ["all"]
199+
});
200+
});
201+
202+
browserAppData.contextMenus.onClicked.addListener((info, tab) => {
203+
if (info.menuItemId === "toggle-ai-inspect" && tab) {
204+
toggle(tab);
205+
}
206+
});

Apps/Web/aiplugin/content.js

Lines changed: 213 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,213 @@
1+
function injectInspectorUI() {
2+
// headless check
3+
if (/Headless/i.test(navigator.userAgent)) {
4+
console.log("Headless mode detected. ZeuZ AI Inspector UI skipped.");
5+
return;
6+
}
7+
8+
const host = document.createElement('div');
9+
host.id = 'zeuz-ai-inspector-host';
10+
11+
// initial position (fixed)
12+
Object.assign(host.style, {
13+
position: 'fixed',
14+
bottom: '20px',
15+
right: '20px',
16+
zIndex: '2147483647', // maximum z-index
17+
width: 'auto',
18+
height: 'auto',
19+
filter: 'drop-shadow(0 4px 6px rgba(0,0,0,0.15))'
20+
});
21+
22+
document.body.appendChild(host);
23+
24+
// shadow dom
25+
const shadow = host.attachShadow({ mode: 'open' });
26+
27+
const style = document.createElement('style');
28+
style.textContent = `
29+
:host {
30+
font-family: sans-serif;
31+
}
32+
.container {
33+
position: relative;
34+
display: flex;
35+
flex-direction: column;
36+
align-items: center;
37+
cursor: grab; /* Cursor indicates draggable */
38+
user-select: none;
39+
}
40+
.container:active {
41+
cursor: grabbing;
42+
}
43+
44+
/* The Main Button */
45+
.ai-fab {
46+
width: 56px;
47+
height: 56px;
48+
background: #d3a8ffff;
49+
border-radius: 50%;
50+
border: 2px solid #fff;
51+
display: flex;
52+
align-items: center;
53+
justify-content: center;
54+
font-size: 28px;
55+
color: white;
56+
transition: all 0.2s ease;
57+
cursor: pointer;
58+
}
59+
60+
.ai-fab img {
61+
pointer-events: none;
62+
}
63+
64+
/* Active State */
65+
.ai-fab.active {
66+
background: #ff8d8dff;
67+
animation: pulse 2s infinite;
68+
cursor: default;
69+
}
70+
71+
/* Hover Effect */
72+
.container:hover .ai-fab {
73+
transform: scale(1.05);
74+
}
75+
76+
/* Close Button */
77+
.close-btn {
78+
position: absolute;
79+
top: -8px;
80+
right: -8px;
81+
width: 20px;
82+
height: 20px;
83+
background: #4b5563;
84+
color: #fff;
85+
border-radius: 50%;
86+
display: flex;
87+
align-items: center;
88+
justify-content: center;
89+
font-size: 12px;
90+
font-weight: bold;
91+
cursor: pointer;
92+
opacity: 0; /* Hidden by default */
93+
transition: opacity 0.2s;
94+
border: 2px solid white;
95+
}
96+
97+
/* close button on hover */
98+
.container:hover .close-btn {
99+
opacity: 1;
100+
}
101+
102+
@keyframes pulse {
103+
0% { box-shadow: 0 0 0 0 rgba(220, 38, 38, 0.7); }
104+
70% { box-shadow: 0 0 0 10px rgba(220, 38, 38, 0); }
105+
100% { box-shadow: 0 0 0 0 rgba(220, 38, 38, 0); }
106+
}
107+
`;
108+
shadow.appendChild(style);
109+
110+
const container = document.createElement('div');
111+
container.className = 'container';
112+
113+
// main button
114+
const btn = document.createElement('div');
115+
btn.className = 'ai-fab';
116+
const btnImg = document.createElement('img');
117+
btnImg.src = chrome.runtime.getURL('zeuz.png');
118+
btnImg.style.width = '32px';
119+
btnImg.style.height = '32px';
120+
btn.appendChild(btnImg);
121+
122+
// close btn
123+
const closeBtn = document.createElement('div');
124+
closeBtn.className = 'close-btn';
125+
closeBtn.innerHTML = '✕';
126+
127+
closeBtn.addEventListener('click', (e) => {
128+
e.stopPropagation();
129+
host.remove(); // remove the whole UI
130+
});
131+
132+
container.appendChild(btn);
133+
container.appendChild(closeBtn);
134+
shadow.appendChild(container);
135+
136+
// drag
137+
let isDragging = false;
138+
let hasMoved = false;
139+
let startX, startY;
140+
141+
const onMouseDown = (e) => {
142+
// don't drag if inspector is active
143+
if (btn.classList.contains('active')) return;
144+
145+
isDragging = true;
146+
hasMoved = false;
147+
148+
const rect = host.getBoundingClientRect();
149+
150+
host.style.right = 'auto';
151+
host.style.bottom = 'auto';
152+
host.style.left = rect.left + 'px';
153+
host.style.top = rect.top + 'px';
154+
155+
startX = e.clientX;
156+
startY = e.clientY;
157+
158+
e.preventDefault();
159+
};
160+
161+
const onMouseMove = (e) => {
162+
if (!isDragging) return;
163+
164+
const dx = e.clientX - startX;
165+
const dy = e.clientY - startY;
166+
167+
if (Math.abs(dx) > 3 || Math.abs(dy) > 3) {
168+
hasMoved = true;
169+
}
170+
171+
host.style.left = (host.offsetLeft + dx) + 'px';
172+
host.style.top = (host.offsetTop + dy) + 'px';
173+
174+
startX = e.clientX;
175+
startY = e.clientY;
176+
};
177+
178+
const onMouseUp = () => {
179+
isDragging = false;
180+
};
181+
182+
// drag listeners
183+
container.addEventListener('mousedown', onMouseDown);
184+
window.addEventListener('mousemove', onMouseMove);
185+
window.addEventListener('mouseup', onMouseUp);
186+
187+
btn.addEventListener('click', (e) => {
188+
if (!hasMoved && !btn.classList.contains('active')) {
189+
chrome.runtime.sendMessage({ action: 'toggle_from_content_script' });
190+
}
191+
hasMoved = false; // Reset after click
192+
});
193+
194+
// right-click context menu for deactivation when inspector is active
195+
btn.addEventListener('contextmenu', (e) => {
196+
e.preventDefault();
197+
if (btn.classList.contains('active')) {
198+
chrome.runtime.sendMessage({ action: 'toggle_from_content_script' });
199+
}
200+
});
201+
202+
chrome.runtime.onMessage.addListener((request) => {
203+
if (request.action === 'activate') {
204+
btn.classList.add('active');
205+
btnImg.src = chrome.runtime.getURL('zeuz-active.png');
206+
} else if (request.action === 'deactivate') {
207+
btn.classList.remove('active');
208+
btnImg.src = chrome.runtime.getURL('zeuz.png');
209+
}
210+
});
211+
}
212+
213+
injectInspectorUI();

0 commit comments

Comments
 (0)