Skip to content

Commit 4d120f9

Browse files
committed
BETA button heuristics - not tested
1 parent a42f357 commit 4d120f9

File tree

2 files changed

+130
-30
lines changed

2 files changed

+130
-30
lines changed

modules/selector-auto-detector/base-heuristics.js

Lines changed: 112 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -64,11 +64,120 @@ window.OneClickPromptsSelectorAutoDetectorBase = {
6464
* Attempts to find the send button using heuristics.
6565
* @returns {HTMLElement|null} The found send button element or null.
6666
*/
67+
6768
detectSendButton: async function () {
6869
logConCgp('[SelectorAutoDetector] Running send button heuristics...');
69-
// Placeholder for future logic:
70-
// 1. Find all buttons
71-
// 2. Score based on icon (SVG), aria-label ('Send', 'Submit'), position relative to editor
70+
71+
// 1. Find all potential candidates
72+
const candidates = [
73+
...document.querySelectorAll('button'),
74+
...document.querySelectorAll('[role="button"]'),
75+
...document.querySelectorAll('div[onclick]'), // Sometimes used
76+
...document.querySelectorAll('span[onclick]')
77+
];
78+
79+
logConCgp(`[SelectorAutoDetector] Found ${candidates.length} initial send button candidates.`);
80+
81+
// 2. Filter for visibility and size
82+
const visibleCandidates = candidates.filter(el => {
83+
const rect = el.getBoundingClientRect();
84+
const style = window.getComputedStyle(el);
85+
86+
const isVisible = style.display !== 'none' &&
87+
style.visibility !== 'hidden' &&
88+
style.opacity !== '0' &&
89+
rect.width > 10 &&
90+
rect.height > 10;
91+
92+
return isVisible;
93+
});
94+
95+
if (visibleCandidates.length === 0) {
96+
logConCgp('[SelectorAutoDetector] No visible send button candidates found.');
97+
return null;
98+
}
99+
100+
// 3. Try to find the editor to use for proximity scoring
101+
let editor = null;
102+
try {
103+
// We use the guard here, but we need to be careful not to create infinite loops if guard calls us.
104+
// Ideally, we should use the base heuristic or a cached reference.
105+
// For now, let's try to find the editor using the base heuristic if we can't find it easily.
106+
editor = await this.detectEditor();
107+
} catch (e) {
108+
// Ignore error
109+
}
110+
111+
const editorRect = editor ? editor.getBoundingClientRect() : null;
112+
113+
// 4. Score candidates
114+
const scoredCandidates = visibleCandidates.map(el => {
115+
let score = 0;
116+
const text = (el.innerText || '').toLowerCase();
117+
const ariaLabel = (el.getAttribute('aria-label') || '').toLowerCase();
118+
const title = (el.getAttribute('title') || '').toLowerCase();
119+
const testId = (el.getAttribute('data-testid') || '').toLowerCase();
120+
121+
// A. Text/Label Matches
122+
const keywords = ['send', 'submit', 'enter', 'chat'];
123+
const matchesKeyword = (str) => keywords.some(k => str.includes(k));
124+
125+
if (matchesKeyword(ariaLabel)) score += 10;
126+
if (matchesKeyword(title)) score += 5;
127+
if (matchesKeyword(testId)) score += 8;
128+
if (text === 'send' || text === 'submit') score += 5; // Exact match
129+
130+
// B. Iconography (SVG presence)
131+
// Modern chat apps almost always use an SVG icon for the send button
132+
if (el.querySelector('svg') || el.tagName.toLowerCase() === 'svg') {
133+
score += 5;
134+
}
135+
136+
// C. Proximity to Editor (if editor found)
137+
// Send buttons are usually to the right or bottom-right of the editor
138+
if (editorRect) {
139+
const btnRect = el.getBoundingClientRect();
140+
141+
// Check if it's inside the editor container or very close
142+
const verticalDist = Math.abs(btnRect.top - editorRect.top);
143+
const horizontalDist = btnRect.left - editorRect.right;
144+
145+
// Inside or overlapping vertically
146+
if (btnRect.top >= editorRect.top && btnRect.bottom <= editorRect.bottom) {
147+
score += 5;
148+
}
149+
150+
// To the right
151+
if (btnRect.left >= editorRect.left) {
152+
score += 3;
153+
}
154+
155+
// Bottom right area is prime real estate for send buttons
156+
if (btnRect.top > editorRect.top && btnRect.left > editorRect.left) {
157+
score += 2;
158+
}
159+
}
160+
161+
// D. State (Disabled?)
162+
// If it's disabled, it might be the send button waiting for input
163+
if (el.disabled || el.getAttribute('aria-disabled') === 'true') {
164+
// Slight boost because it behaves like a send button
165+
score += 1;
166+
}
167+
168+
return { el, score };
169+
});
170+
171+
// 5. Sort by score
172+
scoredCandidates.sort((a, b) => b.score - a.score);
173+
174+
if (scoredCandidates.length > 0 && scoredCandidates[0].score > 0) {
175+
const best = scoredCandidates[0];
176+
logConCgp(`[SelectorAutoDetector] Best send button match:`, best);
177+
return best.el;
178+
}
179+
180+
logConCgp('[SelectorAutoDetector] No high-scoring send button candidates found.');
72181
return null;
73182
}
74183
};

per-website-button-clicking-mechanics/buttons-clicking-aistudio.js

Lines changed: 18 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -8,39 +8,27 @@
88
* @param {string} customText - The custom text to be sent.
99
* @param {boolean} autoSend - Flag indicating whether autosend is enabled.
1010
*/
11-
function processAIStudioCustomSendButtonClick(event, customText, autoSend) {
11+
async function processAIStudioCustomSendButtonClick(event, customText, autoSend) {
1212
// Prevent default button behavior
1313
event.preventDefault();
1414
logConCgp('[AIStudio] Starting process with text:', customText);
1515

1616
const injectionTargets = window.InjectionTargetsOnWebsite;
1717
const aiStudioSelectors = injectionTargets.selectors;
1818

19-
let editorArea = null;
20-
// Try each editor selector until we find one that works
21-
for (const selector of aiStudioSelectors.editors) {
22-
try {
23-
const found = document.querySelector(selector);
24-
if (found) {
25-
editorArea = found;
26-
logConCgp('[AIStudio] Found editor using selector:', selector);
27-
break;
28-
}
29-
} catch (e) {
30-
logConCgp('[AIStudio] Invalid selector:', selector, e);
31-
}
32-
}
19+
// Use SelectorGuard to find the editor (enables heuristics)
20+
const editorArea = await window.OneClickPromptsSelectorGuard.findEditor();
3321

34-
let sendButton = null;
22+
if (editorArea) {
23+
logConCgp('[AIStudio] Found editor using SelectorGuard');
24+
}
3525

36-
// Find send button using selectors from configuration
37-
for (const selector of aiStudioSelectors.sendButtons) {
38-
const foundButton = document.querySelector(selector);
39-
if (foundButton) {
40-
sendButton = foundButton;
41-
logConCgp('[buttons] AI Studio Send button found:', selector);
42-
break;
43-
}
26+
// Use SelectorGuard to find the send button (enables heuristics)
27+
// Note: We find it early to check existence, but we'll find it again during auto-send
28+
// to ensure we have the latest reference if the DOM updated.
29+
const sendButton = await window.OneClickPromptsSelectorGuard.findSendButton();
30+
if (sendButton) {
31+
logConCgp('[buttons] AI Studio Send button found using SelectorGuard');
4432
}
4533

4634
if (!editorArea) {
@@ -66,9 +54,12 @@ function processAIStudioCustomSendButtonClick(event, customText, autoSend) {
6654
if (globalMaxExtensionConfig.globalAutoSendEnabled && autoSend) {
6755
logConCgp('[buttons] AI Studio Auto-send enabled, attempting to send message');
6856
// Use setTimeout to ensure text is processed before sending
69-
setTimeout(() => {
70-
if (sendButton) {
71-
MaxExtensionUtils.simulateClick(sendButton);
57+
// Use setTimeout to ensure text is processed before sending
58+
setTimeout(async () => {
59+
// Re-fetch send button to be safe (and use heuristics if needed)
60+
const btn = await window.OneClickPromptsSelectorGuard.findSendButton();
61+
if (btn) {
62+
MaxExtensionUtils.simulateClick(btn);
7263
} else {
7364
logConCgp('[buttons] AI Studio Send button not found for auto-send.');
7465
showToast('Could not find the send button.', 'error');

0 commit comments

Comments
 (0)