refactor(bridge): migrate gravity bridge to pure websocket gateway architecture, deleting legacy local file scanners and dependencies
This commit is contained in:
@@ -1,6 +1,6 @@
|
||||
export function generateApprovalObserverScript(_port: number): string {
|
||||
return `
|
||||
// ── Gravity Bridge v4: React Tailwind UI Observer ──
|
||||
// ── Gravity Bridge v5: Context-First DOM Extraction ──
|
||||
(function(){
|
||||
'use strict';
|
||||
var BASE='',_obs=false,_sent={},_ready=false;
|
||||
@@ -9,7 +9,7 @@ export function generateApprovalObserverScript(_port: number): string {
|
||||
var CLEANUP_MS=300000;
|
||||
|
||||
function log(m){console.log('[GB Observer] '+m);}
|
||||
log('v4 Script loaded — deep Tailwind DOM traversal enabled');
|
||||
log('v5 Script loaded — Context-First Tailored Extraction');
|
||||
|
||||
// React-Compatible Synthetic Clicker
|
||||
function dispatchReactClick(el){
|
||||
@@ -21,19 +21,10 @@ export function generateApprovalObserverScript(_port: number): string {
|
||||
el.dispatchEvent(new MouseEvent('mouseup', {bubbles:true, cancelable:true, view:window, composed:true}));
|
||||
el.dispatchEvent(new MouseEvent('click', {bubbles:true, cancelable:true, view:window, composed:true}));
|
||||
} catch(e) {
|
||||
el.click(); // fallback
|
||||
el.click();
|
||||
}
|
||||
}
|
||||
|
||||
// ── Find common container for the step ──
|
||||
function findButtonContainer(btn){
|
||||
return btn.closest('.p-1')
|
||||
|| btn.closest('.bg-agent-convo-background')
|
||||
|| btn.closest('[class*="border-gray-500/10"]')
|
||||
|| btn.closest('.monaco-list-row')
|
||||
|| btn.parentElement;
|
||||
}
|
||||
|
||||
function cleanButtonText(btn) {
|
||||
if (!btn) return '';
|
||||
var clone = btn.cloneNode(true);
|
||||
@@ -43,10 +34,9 @@ export function generateApprovalObserverScript(_port: number): string {
|
||||
}
|
||||
var tr = clone.querySelector('.truncate');
|
||||
var txt = (tr ? tr.textContent : clone.textContent) || '';
|
||||
return txt.trim().replace(/^[\s\u200B-\u200D\uFEFF\u00A0]+/, '').replace(/(Alt|Ctrl|Shift|Meta)\\+.*/i,'').trim();
|
||||
return txt.trim().replace(/^[\s\u200B-\u200D\uFEFF\u00A0]+/, '').replace(/(Alt|Ctrl|Shift|Meta)\\\\+.*/i,'').trim();
|
||||
}
|
||||
|
||||
// ── Stable button fingerprint ──
|
||||
function btnId(b,type){
|
||||
var txt = cleanButtonText(b);
|
||||
var parent = b.parentElement;
|
||||
@@ -58,104 +48,78 @@ export function generateApprovalObserverScript(_port: number): string {
|
||||
return type+'|'+txt+'|'+idx;
|
||||
}
|
||||
|
||||
// ── Context extraction — target BOTH chat history and command payload ──
|
||||
function extractCommandContext(b){
|
||||
var container = findButtonContainer(b);
|
||||
var container = b.closest('.p-1') || b.parentElement.parentElement;
|
||||
if (!container) return "";
|
||||
|
||||
var titleSpans = container.querySelectorAll('span[title^="command("]');
|
||||
if (titleSpans && titleSpans.length > 0) {
|
||||
var t = titleSpans[0].getAttribute('title');
|
||||
if (t && t.length > 5) return t.substring(0, 800);
|
||||
}
|
||||
|
||||
var preEls = container.querySelectorAll('pre');
|
||||
if (preEls && preEls.length > 0) {
|
||||
var t2 = (preEls[preEls.length-1].textContent || '').trim();
|
||||
if (t2.length > 2) return t2.substring(0, 800);
|
||||
}
|
||||
|
||||
var codeText = '';
|
||||
var codes = container.querySelectorAll('code, [class*="command"]');
|
||||
for(var i=0; i<codes.length; i++) {
|
||||
codeText += (codes[i].textContent || '').trim() + ' ';
|
||||
}
|
||||
if (codeText.length > 2) return codeText.trim().substring(0, 800);
|
||||
|
||||
var fallback = (container.textContent || '').replace(cleanButtonText(b), '').trim();
|
||||
return fallback.substring(0, 500);
|
||||
}
|
||||
|
||||
function extractChatContextFromNode(botTurn) {
|
||||
if (!botTurn) return '';
|
||||
|
||||
var res = '';
|
||||
// Use innerText if available on the markdown container (preserves spacing perfectly)
|
||||
var md = botTurn.querySelector('.markdown-body') || botTurn.querySelector('.prose');
|
||||
if (md && md.innerText && md.innerText.trim().length > 10) {
|
||||
res = md.innerText.trim();
|
||||
return res.substring(0, 3500);
|
||||
}
|
||||
|
||||
var toolContainer = botTurn.querySelector('.bg-agent-convo-background') || botTurn.querySelector('.bg-ide-background-color');
|
||||
var textParts = [];
|
||||
function walk(node) {
|
||||
if (toolContainer && node === toolContainer) return;
|
||||
if (node.id === 'antigravity.agentSidePanelInputBox') return;
|
||||
if (node.nodeType === 1) {
|
||||
var tag = node.tagName.toUpperCase();
|
||||
if (tag==='BUTTON' || tag==='SVG' || tag==='STYLE' || tag==='SCRIPT') return;
|
||||
// Skip tool action blocks aggressively if they masquerade as normal divs
|
||||
if (node !== botTurn && node.classList && (node.classList.contains('bg-ide-background-color') || node.classList.contains('bg-agent-convo-background'))) return;
|
||||
}
|
||||
if (node.nodeType === 3) {
|
||||
var val = node.nodeValue;
|
||||
if (val && val.trim()) textParts.push(val.trim());
|
||||
} else {
|
||||
if (node.childNodes && node.childNodes.length > 0) {
|
||||
for(var i=0; i<node.childNodes.length; i++) {
|
||||
walk(node.childNodes[i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (node.nodeType === 1) {
|
||||
var tg = node.tagName.toUpperCase();
|
||||
if (tg==='P' || tg==='DIV' || tg==='BR' || tg==='LI' || tg==='PRE') textParts.push('\\n');
|
||||
}
|
||||
}
|
||||
walk(botTurn);
|
||||
res = textParts.join(' ').replace(/ \\n /g, '\\n').replace(/\\n+/g, '\\n').trim();
|
||||
return res.substring(0, 3500);
|
||||
}
|
||||
|
||||
function extractChatContext(b) {
|
||||
try {
|
||||
var botTurn = b.closest('.bg-agent-convo-background') || b.closest('.text-ide-message-block-bot-color');
|
||||
var botTurn = b.closest('.text-ide-message-block-bot-color') || b.closest('.bg-agent-convo-background');
|
||||
if (!botTurn) {
|
||||
var container = findButtonContainer(b);
|
||||
var container = b.closest('.p-1') || b.parentElement;
|
||||
botTurn = container ? container.parentElement : null;
|
||||
}
|
||||
if (!botTurn) return '';
|
||||
|
||||
var toolContainer = findButtonContainer(b) || b;
|
||||
var textParts = [];
|
||||
|
||||
function walk(node) {
|
||||
if (node === toolContainer) return true; // Stop traversal at the tool box
|
||||
if (node.nodeType === 1) {
|
||||
var tag = node.tagName.toUpperCase();
|
||||
if (tag==='BUTTON' || tag==='SVG' || tag==='STYLE' || tag==='SCRIPT') return false;
|
||||
}
|
||||
if (node.nodeType === 3) {
|
||||
var val = node.nodeValue;
|
||||
if (val && val.trim()) textParts.push(val.trim());
|
||||
} else {
|
||||
for(var i=0; i<node.childNodes.length; i++) {
|
||||
if (walk(node.childNodes[i])) return true;
|
||||
}
|
||||
}
|
||||
if (node.nodeType === 1) {
|
||||
var tg = node.tagName.toUpperCase();
|
||||
if (tg==='P' || tg==='DIV' || tg==='BR' || tg==='LI' || tg==='PRE') textParts.push('\\n');
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
walk(botTurn);
|
||||
var result = textParts.join(' ').replace(/ \\n /g, '\\n').replace(/\\n+/g, '\\n').trim();
|
||||
return result.substring(0, 1500);
|
||||
return extractChatContextFromNode(botTurn);
|
||||
} catch(e) {
|
||||
return '';
|
||||
}
|
||||
}
|
||||
|
||||
function extractChatContextFromNode(botTurn) {
|
||||
if (!botTurn) return '';
|
||||
var toolContainer = botTurn.querySelector('.bg-ide-background-color'); // Stop at tool blocks
|
||||
var textParts = [];
|
||||
function walk(node) {
|
||||
if (toolContainer && node === toolContainer) return true;
|
||||
if (node.nodeType === 1) {
|
||||
var tag = node.tagName.toUpperCase();
|
||||
if (tag==='BUTTON' || tag==='SVG' || tag==='STYLE' || tag==='SCRIPT') return false;
|
||||
}
|
||||
if (node.nodeType === 3) {
|
||||
var val = node.nodeValue;
|
||||
if (val && val.trim()) textParts.push(val.trim());
|
||||
} else {
|
||||
for(var i=0; i<node.childNodes.length; i++) {
|
||||
if (walk(node.childNodes[i])) return true;
|
||||
}
|
||||
}
|
||||
if (node.nodeType === 1) {
|
||||
var tg = node.tagName.toUpperCase();
|
||||
if (tg==='P' || tg==='DIV' || tg==='BR' || tg==='LI' || tg==='PRE') textParts.push('\\n');
|
||||
}
|
||||
return false;
|
||||
}
|
||||
walk(botTurn);
|
||||
var result = textParts.join(' ').replace(/ \\n /g, '\\n').replace(/\\n+/g, '\\n').trim();
|
||||
return result.substring(0, 3500);
|
||||
}
|
||||
|
||||
function extractContext(b) {
|
||||
var cmd = extractCommandContext(b);
|
||||
var chat = extractChatContext(b);
|
||||
@@ -166,15 +130,21 @@ export function generateApprovalObserverScript(_port: number): string {
|
||||
return combined.trim();
|
||||
}
|
||||
|
||||
// ── Action Buttons Patterns (EN / KO) ──
|
||||
var PATS = [
|
||||
{ type: 'command', re: /^(?:Always\\s*)?(?:Run\\b|결행사양\\s*항상|결행)/i },
|
||||
{ type: 'permission', re: /^(?:Always\\s*)?(?:Allow\\b|허용)/i },
|
||||
{ type: 'permission', re: /^(?:Always\\s*)?(?:Approve\\b|승인)/i },
|
||||
{ type: 'diff_review', re: /^(?:Always\\s*)?(?:Accept\\b|수락|반영)/i },
|
||||
];
|
||||
var ALL_ACTION_RE=[/^(?:Always\\s*)?(?:Run\\b|결행)/i,/^(?:Always\\s*)?(?:Accept\\b|수락|반영)/i,/^(?:Reject\\b|거절|거부)/i,/^(?:Always\\s*)?(?:Allow\\b|허용)/i,/^(?:Deny\\b|차단)/i,/^(?:Always\\s*)?(?:Approve\\b|승인)/i,/^(?:Cancel\\b|취소)/i,/^Retry\\b/i,/^(?:Dismiss\\b|무시)/i,/^(?:Stop\\b|정지)/i,/^Decline\\b/i];
|
||||
var REJECT_RE=[/^(?:Reject\\b|거절|거부)/i,/^(?:Cancel\\b|취소)/i,/^(?:Deny\\b|차단)/i,/^(?:Stop\\b|정지)/i,/^Decline\\b/i,/^(?:Dismiss\\b|무시)/i];
|
||||
var ACTION_WORDS = ['Allow', 'Run', 'Approve', 'Accept', '결행', '수락', '반영', '허용', '승인'];
|
||||
var REJECT_WORDS = ['Reject', 'Cancel', 'Deny', 'Stop', 'Decline', 'Dismiss', '거절', '거부', '취소', '차단', '정지', '무시'];
|
||||
|
||||
function isActionBtn(txt) {
|
||||
for(var i=0; i<ACTION_WORDS.length; i++) {
|
||||
if(txt.indexOf(ACTION_WORDS[i]) !== -1) return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
function isRejectBtn(txt) {
|
||||
for(var i=0; i<REJECT_WORDS.length; i++) {
|
||||
if(txt.indexOf(REJECT_WORDS[i]) !== -1) return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
function collectSiblingButtons(container,triggerBtn){
|
||||
if(!container)return [];
|
||||
@@ -183,110 +153,86 @@ export function generateApprovalObserverScript(_port: number): string {
|
||||
for(var i=0;i<siblings.length;i++){
|
||||
var sb=siblings[i];
|
||||
if(sb.disabled||sb.hidden||(!sb.offsetParent&&sb.style.display!=='fixed'))continue;
|
||||
|
||||
var stxt = cleanButtonText(sb);
|
||||
if(stxt.length <= 1) continue; // Ignore icon buttons
|
||||
|
||||
var isAction=false;
|
||||
for(var a=0;a<ALL_ACTION_RE.length;a++){
|
||||
if(ALL_ACTION_RE[a].test(stxt)){isAction=true;break;}
|
||||
}
|
||||
if(!isAction)continue;
|
||||
if(stxt.length <= 1) continue;
|
||||
if(!isActionBtn(stxt) && !isRejectBtn(stxt)) continue;
|
||||
result.push({btn:sb,text:stxt,isPrimary:(sb===triggerBtn)});
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
var HARDCODED_PORT=${_port};
|
||||
|
||||
function tryPingAsync(port){
|
||||
return fetch('http://127.0.0.1:'+port+'/ping?t='+Date.now(),{signal:AbortSignal.timeout(2000)})
|
||||
.then(function(r){return r.text();})
|
||||
.then(function(t){return t==='pong';})
|
||||
.catch(function(){return false;});
|
||||
.then(function(r){return r.text();}).then(function(t){return t==='pong';}).catch(function(){return false;});
|
||||
}
|
||||
|
||||
function discoverPort(cb){
|
||||
log('Waiting for Gravity Bridge status...');
|
||||
var attempts=0;
|
||||
var timer=setInterval(function(){
|
||||
attempts++;
|
||||
var items = document.querySelectorAll('[aria-label^="Gravity Bridge Control"], [title^="Gravity Bridge Control"]');
|
||||
if (items.length > 0) {
|
||||
var text = items[0].getAttribute('aria-label') || items[0].getAttribute('title') || '';
|
||||
var m = text.match(/port:(\d+)/);
|
||||
var m = text.match(/port:(\\d+)/);
|
||||
if (m && m[1]) {
|
||||
var domPort = parseInt(m[1], 10);
|
||||
clearInterval(timer);
|
||||
tryPingAsync(domPort).then(function(ok){
|
||||
if(ok) cb(domPort); else cb(HARDCODED_PORT);
|
||||
});
|
||||
tryPingAsync(parseInt(m[1], 10)).then(function(ok){ cb(ok ? parseInt(m[1],10) : HARDCODED_PORT); });
|
||||
return;
|
||||
}
|
||||
}
|
||||
// If we are in the webview, the status bar is invisible. Skip quickly.
|
||||
if(attempts>1){
|
||||
clearInterval(timer);
|
||||
tryPingAsync(HARDCODED_PORT).then(function(ok){ cb(HARDCODED_PORT); }); // Assume HARDCODED_PORT works!
|
||||
tryPingAsync(HARDCODED_PORT).then(function(){ cb(HARDCODED_PORT); });
|
||||
}
|
||||
},500); // Wait 500ms * 2 = 1 second total
|
||||
},500);
|
||||
}
|
||||
|
||||
discoverPort(function(port){
|
||||
BASE='http://127.0.0.1:'+port;
|
||||
fetch(BASE+'/ping').then(function(r){return r.text();}).then(function(t){
|
||||
if(t==='pong'){_ready=true;startObserver();}
|
||||
}).catch(function(e){});
|
||||
_ready=true;
|
||||
startObserver();
|
||||
});
|
||||
|
||||
var _chatSnapshots = [];
|
||||
var _firstChatScan = true;
|
||||
var _lastText = "";
|
||||
var _lastTextTime = 0;
|
||||
var _lastTextSent = false;
|
||||
|
||||
function scanChatBodies() {
|
||||
if(!_ready)return;
|
||||
var botTurns = document.querySelectorAll('.text-ide-message-block-bot-color');
|
||||
for (var i = 0; i < botTurns.length; i++) {
|
||||
var turn = botTurns[i];
|
||||
if (turn.dataset.agChatScraped === "true" || turn.dataset.agChatScraped === "pending") continue;
|
||||
|
||||
if (_firstChatScan) {
|
||||
turn.dataset.agChatScraped = "true";
|
||||
continue;
|
||||
}
|
||||
|
||||
var currentText = turn.textContent || '';
|
||||
var found = -1;
|
||||
for (var j = 0; j < _chatSnapshots.length; j++) {
|
||||
if (_chatSnapshots[j].node === turn) { found = j; break; }
|
||||
}
|
||||
|
||||
if (found === -1) {
|
||||
_chatSnapshots.push({ node: turn, text: currentText, lastChanged: Date.now() });
|
||||
} else {
|
||||
if (_chatSnapshots[found].text !== currentText) {
|
||||
_chatSnapshots[found].text = currentText;
|
||||
_chatSnapshots[found].lastChanged = Date.now();
|
||||
if (botTurns.length === 0) return;
|
||||
|
||||
var lastTurn = botTurns[botTurns.length - 1];
|
||||
if (lastTurn.dataset.agChatScraped === "true" || lastTurn.dataset.agChatScraped === "pending") return;
|
||||
|
||||
var currentText = lastTurn.textContent || '';
|
||||
if (currentText.length < 5) return;
|
||||
|
||||
if (_lastText !== currentText) {
|
||||
_lastText = currentText;
|
||||
_lastTextTime = Date.now();
|
||||
_lastTextSent = false;
|
||||
} else if (!_lastTextSent) {
|
||||
if (Date.now() - _lastTextTime > 3000) {
|
||||
_lastTextSent = true;
|
||||
lastTurn.dataset.agChatScraped = "pending";
|
||||
var finalTxt = extractChatContextFromNode(lastTurn);
|
||||
if (finalTxt && finalTxt.length > 5 && finalTxt !== "Review Changes") {
|
||||
fetch(BASE+'/chat', {
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify({ text: finalTxt })
|
||||
}).then(function(){
|
||||
lastTurn.dataset.agChatScraped = "true";
|
||||
}).catch(function(){
|
||||
lastTurn.dataset.agChatScraped = "false";
|
||||
});
|
||||
} else {
|
||||
if (Date.now() - _chatSnapshots[found].lastChanged > 3500) {
|
||||
turn.dataset.agChatScraped = "pending"; // prevent re-entry
|
||||
var finalTxt = extractChatContextFromNode(turn);
|
||||
if (finalTxt && finalTxt.length > 5) {
|
||||
fetch(BASE+'/chat', {
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify({ text: finalTxt })
|
||||
}).then(function(){
|
||||
turn.dataset.agChatScraped = "true";
|
||||
}).catch(function(){
|
||||
turn.dataset.agChatScraped = "false"; // retry
|
||||
});
|
||||
} else {
|
||||
turn.dataset.agChatScraped = "true";
|
||||
}
|
||||
}
|
||||
lastTurn.dataset.agChatScraped = "true";
|
||||
}
|
||||
}
|
||||
}
|
||||
_firstChatScan = false;
|
||||
}
|
||||
|
||||
function scan(){
|
||||
@@ -301,26 +247,17 @@ export function generateApprovalObserverScript(_port: number): string {
|
||||
if(b.disabled||b.hidden||(!b.offsetParent&&b.style.display!=='fixed'))continue;
|
||||
|
||||
var txt=cleanButtonText(b);
|
||||
console.log("[JSDOM] Button scan:", txt);
|
||||
if(txt.length <= 1) continue; // Icon
|
||||
if(txt.length <= 1) continue;
|
||||
|
||||
var matchedType=null;
|
||||
for(var p=0;p<PATS.length;p++){
|
||||
if(PATS[p].re.test(txt)){
|
||||
if (b.closest('.codelens-decoration') && PATS[p].type !== 'diff_review' && PATS[p].type !== 'permission') {
|
||||
continue;
|
||||
}
|
||||
matchedType=PATS[p].type;
|
||||
break;
|
||||
}
|
||||
if(!isActionBtn(txt)) continue;
|
||||
// Skip inline code lens buttons unless they actually match the pattern properly
|
||||
if (b.closest('.codelens-decoration') && !txt.includes('Accept') && !txt.includes('수락') && !txt.includes('반영')) {
|
||||
continue;
|
||||
}
|
||||
if(!matchedType){
|
||||
console.log("[JSDOM] NOT MATCHED:", txt);
|
||||
continue;
|
||||
}
|
||||
var container=findButtonContainer(b);
|
||||
|
||||
var matchedType = txt.includes('Accept') ? 'diff_review' : (txt.includes('Run') ? 'command' : 'permission');
|
||||
var container=b.closest('.p-1') || b.parentElement.parentElement;
|
||||
var groupKey=matchedType+'|'+btnId(b,matchedType);
|
||||
console.log("[JSDOM] MATCHED:", matchedType, "KEY:", groupKey, "SENT:", !!_sent[groupKey]);
|
||||
if(_sent[groupKey])continue;
|
||||
|
||||
var siblings=collectSiblingButtons(container,b);
|
||||
@@ -338,7 +275,6 @@ export function generateApprovalObserverScript(_port: number): string {
|
||||
}
|
||||
|
||||
var desc=extractContext(b);
|
||||
|
||||
var is_dom_dummy = false;
|
||||
if (!desc || desc.trim().length <= 2) {
|
||||
desc = "MISSING_DESCRIPTION_FROM_DOM_FALLBACK_TO_STEP_PROBE";
|
||||
@@ -417,17 +353,15 @@ export function generateApprovalObserverScript(_port: number): string {
|
||||
}
|
||||
|
||||
function clickRejectButton(approveBtn){
|
||||
var container=findButtonContainer(approveBtn);
|
||||
var container=approveBtn.closest('.p-1') || approveBtn.parentElement.parentElement;
|
||||
if(!container)return;
|
||||
var siblings=container.querySelectorAll('button');
|
||||
for(var i=0;i<siblings.length;i++){
|
||||
var t=cleanButtonText(siblings[i]);
|
||||
for(var r=0;r<REJECT_RE.length;r++){
|
||||
if(REJECT_RE[r].test(t)){
|
||||
log('Clicking reject: '+t);
|
||||
dispatchReactClick(siblings[i]);
|
||||
return;
|
||||
}
|
||||
if(isRejectBtn(t)){
|
||||
log('Clicking reject: '+t);
|
||||
dispatchReactClick(siblings[i]);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -476,22 +410,17 @@ export function generateApprovalObserverScript(_port: number): string {
|
||||
if(_ready&&BASE){
|
||||
fetch(BASE+'/trigger-click?t='+Date.now()).then(function(r){return r.json();}).then(function(d){
|
||||
if(!d.action)return;
|
||||
var approveRe=[/^(?:Always\\\\s*)?(?:Run\\\\b|결행)/i,/^(?:Always\\\\s*)?(?:Accept\\\\b|수락)/i,/^(?:Always\\\\s*)?(?:Accept all\\\\b|모두 수락)/i,/^(?:Always\\\\s*)?(?:Allow\\\\b|허용)/i,/^(?:Always\\\\s*)?(?:Approve\\\\b|승인)/i];
|
||||
var rejectRe=[/^(?:Reject\\\\b|거절|거부)/i,/^(?:Cancel\\\\b|취소)/i,/^(?:Deny\\\\b|차단)/i,/^(?:Stop\\\\b|정지)/i,/^Decline\\\\b/i,/^(?:Dismiss\\\\b|무시)/i];
|
||||
var patterns=(d.action==='approve')?approveRe:rejectRe;
|
||||
|
||||
var isApprove = (d.action==='approve');
|
||||
var btns = document.querySelectorAll('button');
|
||||
for(var i=0;i<btns.length;i++){
|
||||
var bx = btns[i];
|
||||
if(bx.disabled||bx.hidden||(!bx.offsetParent&&bx.style.display!=='fixed'))continue;
|
||||
var t = cleanButtonText(bx);
|
||||
if(t.length <= 1) continue;
|
||||
for(var pi=0;pi<patterns.length;pi++){
|
||||
if(patterns[pi].test(t)){
|
||||
log('Fallback TRIGGER-CLICK on "' + t + '"');
|
||||
dispatchReactClick(bx);
|
||||
return;
|
||||
}
|
||||
if (isApprove ? isActionBtn(t) : isRejectBtn(t)) {
|
||||
log('Fallback TRIGGER-CLICK on "' + t + '"');
|
||||
dispatchReactClick(bx);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}).catch(function(){});
|
||||
|
||||
Reference in New Issue
Block a user