Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
85 changes: 61 additions & 24 deletions Apps/Web/aiplugin/background.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@ const defaultIcon = 'zeuz.png';
var zeuz_url;
var zeuz_key;
var zeuz_node_id;
const _aiContentTimers = {}; // per-tab debounce for node_ai_contents
const _aiContentHashes = {}; // per-tab hashes to avoid resending unchanged contents

fetch("data.json")
.then(Response => Response.json())
Expand Down Expand Up @@ -135,7 +137,7 @@ if (navigator.userAgentData.platform.toLowerCase().includes('mac')) {
}
browserAppData.runtime.onMessage.addListener(
function (request, sender, sendResponse) {

if (request.action === 'toggle_from_content_script') {
// allows the floating button to trigger the toggle logic
toggle(sender.tab);
Expand Down Expand Up @@ -171,36 +173,71 @@ browserAppData.runtime.onMessage.addListener(
.then(text => { console.log(text); sendResponse(text); })

return true; // Will respond asynchronously.
} else if (request.apiName == 'node_ai_contents'){
var url = `${zeuz_url}/node_ai_contents/`;
fetch(url, {
method: "POST",
headers: {
// "Content-Type": "application/json",
"X-Api-Key": zeuz_key,
},
body: JSON.stringify({
"dom_web": { "dom": request.dom },
"node_id": zeuz_node_id
}),
})
.then(response => response.json())
.then(text => { console.log(text); sendResponse(text); })
} else if (request.apiName == 'node_ai_contents') {
const tabId = sender.tab ? sender.tab.id : 'unknown';
if (_aiContentTimers[tabId]) clearTimeout(_aiContentTimers[tabId]);
_aiContentTimers[tabId] = setTimeout(async () => {
delete _aiContentTimers[tabId];

const contentObj = { "dom": request.dom, "page_map": request.page_map, "page_map_json": request.page_map_json };
const contentStr = JSON.stringify(contentObj);

let hash = '';
try {
const msgUint8 = new TextEncoder().encode(contentStr);
const hashBuffer = await crypto.subtle.digest('SHA-256', msgUint8);
const hashArray = Array.from(new Uint8Array(hashBuffer));
hash = hashArray.map(b => b.toString(16).padStart(2, '0')).join('');
} catch (e) {
console.error("Error generating hash", e);
hash = contentStr.length.toString(); // Fallback
}

if (_aiContentHashes[tabId] === hash) {
console.log('node_ai_contents skipped, content unchanged for tab:', tabId, 'hash:', hash);
try { sendResponse({ status: "skipped" }); } catch (e) { }
return;
}
_aiContentHashes[tabId] = hash;
console.log('node_ai_contents sending, new hash for tab:', tabId, 'hash:', hash);

var url = `${zeuz_url}/node_ai_contents/`;
fetch(url, {
method: "POST",
headers: {
// "Content-Type": "application/json",
"X-Api-Key": zeuz_key,
},
body: JSON.stringify({
"dom_web": contentObj,
"node_id": zeuz_node_id
}),
})
.then(response => {
if (!response.ok) {
console.error("node_ai_contents failed with status:", response.status, response.statusText);
}
return response.json();
})
.then(text => { console.log("node_ai_contents response:", text); try { sendResponse(text); } catch (e) { } })
.catch(e => console.error("node_ai_contents fetch error:", e));
}, 2000);
return true; // Will respond asynchronously.
}
}
);

// add AI Inspector to the right click menu
browserAppData.runtime.onInstalled.addListener(() => {
browserAppData.contextMenus.create({
id: "toggle-ai-inspect",
title: "Inspect with AI",
contexts: ["all"]
});
browserAppData.contextMenus.create({
id: "toggle-ai-inspect",
title: "Inspect with AI",
contexts: ["all"]
});
});

browserAppData.contextMenus.onClicked.addListener((info, tab) => {
if (info.menuItemId === "toggle-ai-inspect" && tab) {
toggle(tab);
}
if (info.menuItemId === "toggle-ai-inspect" && tab) {
toggle(tab);
}
});
39 changes: 27 additions & 12 deletions Apps/Web/aiplugin/inspect.js
Original file line number Diff line number Diff line change
@@ -1,10 +1,14 @@
const browserAppData = chrome || browser;
setInterval(() => {
// Only run this periodic extraction on the main page, not inside iframes
// (this prevents tracking pixels from overwriting the main page DOM)
if (window.top !== window.self) return;

var html = document.createElement('html');
html.setAttribute('zeuz','aiplugin');
html.setAttribute('zeuz', 'aiplugin');
var myString = document.documentElement.outerHTML;
html.innerHTML = myString;

var elements = html.getElementsByTagName('head');
while (elements[0])
elements[0].parentNode.removeChild(elements[0])
Expand All @@ -20,16 +24,27 @@ setInterval(() => {
var elements = html.getElementsByTagName('style');
while (elements[0])
elements[0].parentNode.removeChild(elements[0])

// AI model works better on indented dom, so not removing indentation.
// var result = html.outerHTML.replace(/\s+/g, ' ').replace(/>\s+</g, '><');

//The following code removes non-unicode characters except newline and tab
var result = html.outerHTML.replace(/[\x00-\x08\x0B-\x1F\x7F]/g, '');

let mapData = { page_map_json: null, page_map: "" };
if (typeof extractPageMapData === 'function') {
try {
mapData = extractPageMapData();
} catch (e) {
console.error("Error extracting page map:", e);
}
}

browserAppData.runtime.sendMessage({
apiName: 'node_ai_contents',
dom: result,
page_map: mapData.page_map,
page_map_json: mapData.page_map_json
})

}, 5000);
Expand Down Expand Up @@ -68,7 +83,7 @@ class Inspector {
if (response["info"] == "success") {
const modalText = 'Element data was recorded. Please Click "Add by AI"';
console.log(modalText);

if (this.successContainer) {
this.successContainer.textContent = modalText;
this.successContainer.classList.add('show');
Expand All @@ -88,9 +103,9 @@ class Inspector {
data: data,
html: refinedHtml,
},
response => {
insert_modal_text(response, modal_id);
}
response => {
insert_modal_text(response, modal_id);
}
);
}

Expand Down Expand Up @@ -477,19 +492,19 @@ class Inspector {
let value = e.target.getAttribute(name);
elementText += `${name}="${value}" `;
}

this.attributesContainer.textContent = elementText.trim();
}

activate() {
this.createOverlayElements();
this.createSuccessMessage();

const style = document.createElement('style');
style.id = this.cssNode;
style.textContent = '*{cursor:crosshair!important;}';
document.head.appendChild(style);

// add listeners
document.addEventListener('click', this.getData, true);
this.options.inspector && (document.addEventListener('mouseover', this.draw));
Expand All @@ -506,15 +521,15 @@ class Inspector {
'zeuz-success-host'
]

for (let elemId of Remove){
for (let elemId of Remove) {
const elem = document.getElementById(elemId);
elem && elem.remove();
}

// remove listeners
document.removeEventListener('click', this.getData, true);
this.options && this.options.inspector && (document.removeEventListener('mouseover', this.draw));

// reset
this.attributesHost = null;
this.attributesContainer = null;
Expand Down
1 change: 1 addition & 0 deletions Apps/Web/aiplugin/manifest.json
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@
"https://*/*"
],
"js": [
"page_map_extractor.js",
"inspect.js"
]
},
Expand Down
Loading
Loading