Skip to content

Commit 5747ee1

Browse files
author
mdshakib007
committed
Add AI Inspector UI and context menu integration
- Implemented injectInspectorUI function to create a draggable AI Inspector button. - Added context menu option to trigger the AI Inspector toggle. - Updated manifest.json to include content script and permissions for the new functionality.
1 parent 760153e commit 5747ee1

3 files changed

Lines changed: 229 additions & 2 deletions

File tree

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: 189 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,189 @@
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: #1500ffff;
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+
}
58+
59+
/* Active State */
60+
.ai-fab.active {
61+
background: #ff0000ff;
62+
animation: pulse 2s infinite;
63+
}
64+
65+
/* Hover Effect */
66+
.container:hover .ai-fab {
67+
transform: scale(1.05);
68+
}
69+
70+
/* Close Button */
71+
.close-btn {
72+
position: absolute;
73+
top: -8px;
74+
right: -8px;
75+
width: 20px;
76+
height: 20px;
77+
background: #4b5563;
78+
color: #fff;
79+
border-radius: 50%;
80+
display: flex;
81+
align-items: center;
82+
justify-content: center;
83+
font-size: 12px;
84+
font-weight: bold;
85+
cursor: pointer;
86+
opacity: 0; /* Hidden by default */
87+
transition: opacity 0.2s;
88+
border: 2px solid white;
89+
}
90+
91+
/* close button on hover */
92+
.container:hover .close-btn {
93+
opacity: 1;
94+
}
95+
96+
@keyframes pulse {
97+
0% { box-shadow: 0 0 0 0 rgba(220, 38, 38, 0.7); }
98+
70% { box-shadow: 0 0 0 10px rgba(220, 38, 38, 0); }
99+
100% { box-shadow: 0 0 0 0 rgba(220, 38, 38, 0); }
100+
}
101+
`;
102+
shadow.appendChild(style);
103+
104+
const container = document.createElement('div');
105+
container.className = 'container';
106+
107+
// main button
108+
const btn = document.createElement('div');
109+
btn.className = 'ai-fab';
110+
const btnImg = document.createElement('img');
111+
btnImg.src = 'zeuz.png'
112+
btn.innerHTML = btnImg;
113+
114+
// close btn
115+
const closeBtn = document.createElement('div');
116+
closeBtn.className = 'close-btn';
117+
closeBtn.innerHTML = '✕';
118+
119+
closeBtn.addEventListener('click', (e) => {
120+
e.stopPropagation();
121+
host.remove(); // remove the whole UI
122+
});
123+
124+
container.appendChild(btn);
125+
container.appendChild(closeBtn);
126+
shadow.appendChild(container);
127+
128+
// drag
129+
let isDragging = false;
130+
let hasMoved = false;
131+
let startX, startY, initialRight, initialBottom;
132+
133+
const onMouseDown = (e) => {
134+
isDragging = true;
135+
hasMoved = false;
136+
137+
const rect = host.getBoundingClientRect();
138+
139+
host.style.right = 'auto';
140+
host.style.bottom = 'auto';
141+
host.style.left = rect.left + 'px';
142+
host.style.top = rect.top + 'px';
143+
144+
startX = e.clientX;
145+
startY = e.clientY;
146+
};
147+
148+
const onMouseMove = (e) => {
149+
if (!isDragging) return;
150+
151+
const dx = e.clientX - startX;
152+
const dy = e.clientY - startY;
153+
154+
if (Math.abs(dx) > 3 || Math.abs(dy) > 3) {
155+
hasMoved = true;
156+
}
157+
158+
host.style.left = (host.offsetLeft + dx) + 'px';
159+
host.style.top = (host.offsetTop + dy) + 'px';
160+
161+
startX = e.clientX;
162+
startY = e.clientY;
163+
};
164+
165+
const onMouseUp = () => {
166+
isDragging = false;
167+
};
168+
169+
// drag listeners
170+
container.addEventListener('mousedown', onMouseDown);
171+
window.addEventListener('mousemove', onMouseMove);
172+
window.addEventListener('mouseup', onMouseUp);
173+
174+
btn.addEventListener('click', () => {
175+
if (!hasMoved) {
176+
chrome.runtime.sendMessage({ action: 'toggle_from_content_script' });
177+
}
178+
});
179+
180+
chrome.runtime.onMessage.addListener((request) => {
181+
if (request.action === 'activate') {
182+
btn.classList.add('active');
183+
} else if (request.action === 'deactivate') {
184+
btn.classList.remove('active');
185+
}
186+
});
187+
}
188+
189+
injectInspectorUI();

Apps/Web/aiplugin/manifest.json

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,16 @@
2929
"js": [
3030
"inspect.js"
3131
]
32+
},
33+
{
34+
"all_frames": false,
35+
"matches": [
36+
"http://*/*",
37+
"https://*/*"
38+
],
39+
"js": [
40+
"content.js"
41+
]
3242
}
3343
],
3444
"commands": {
@@ -50,5 +60,11 @@
5060
"host_permissions": [
5161
"http://*/",
5262
"https://*/"
53-
]
63+
],
64+
"web_accessible_resources": [
65+
{
66+
"resources": ["zeuz.png", "zeuz-active.png"],
67+
"matches": ["<all_urls>"]
68+
}
69+
]
5470
}

0 commit comments

Comments
 (0)