Skip to content

Commit 9d39634

Browse files
committed
Gemini injection (not perferct)
1 parent c97c509 commit 9d39634

File tree

4 files changed

+136
-18
lines changed

4 files changed

+136
-18
lines changed

buttons-clicking-gemini.js

Lines changed: 98 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,98 @@
1+
// buttons-clicking-gemini.js
2+
// This file provides functions to handle the send button clicking process for Google Gemini.
3+
'use strict';
4+
5+
/**
6+
* Processes the custom send button click for Google Gemini.
7+
* @param {Event} event - The click event triggered by the custom button.
8+
* @param {string} customText - The custom text to be sent.
9+
* @param {boolean} autoSend - Flag indicating whether autosend is enabled.
10+
*/
11+
function processGeminiCustomSendButtonClick(event, customText, autoSend) {
12+
event.preventDefault();
13+
logConCgp('[Gemini] Custom send button clicked. Processing...');
14+
15+
const injectionTargets = window.InjectionTargetsOnWebsite;
16+
const geminiSelectors = injectionTargets.selectors;
17+
18+
// Find editor area
19+
const editorArea = geminiSelectors.editors.reduce((found, selector) =>
20+
found || document.querySelector(selector), null);
21+
22+
if (!editorArea) {
23+
logConCgp('[Gemini] Editor area not found. Unable to proceed.');
24+
return;
25+
}
26+
27+
// Find send button
28+
const locateSendButton = () => {
29+
return geminiSelectors.sendButtons
30+
.map(selector => document.querySelector(selector))
31+
.find(button => button && button.getAttribute('aria-disabled') !== 'true'); // Only find enabled button
32+
};
33+
34+
let sendButton = locateSendButton();
35+
36+
/**
37+
* Inserts text into the Gemini editor (Quill).
38+
* @param {HTMLElement} editor - The contenteditable editor element.
39+
* @param {string} text - The text to insert.
40+
*/
41+
const insertTextIntoGeminiEditor = (editor, text) => {
42+
editor.focus();
43+
logConCgp('[Gemini] Editor focused.');
44+
45+
// Clear existing placeholder/content if editor is effectively empty
46+
const isInitial = editor.classList.contains('ql-blank') || editor.innerHTML === '<p><br></p>';
47+
const currentText = isInitial ? '' : editor.innerText.trim();
48+
const newText = `${currentText}${text}`;
49+
50+
// Set innerHTML - Quill expects paragraphs
51+
editor.innerHTML = `<p>${newText.replace(/\n/g, '</p><p>')}</p>`;
52+
logConCgp('[Gemini] Editor innerHTML updated.');
53+
54+
// Dispatch events to notify the framework (likely Angular)
55+
// 'input' is crucial for Quill/Angular to recognize the change
56+
editor.dispatchEvent(new Event('input', { bubbles: true, composed: true }));
57+
editor.dispatchEvent(new Event('change', { bubbles: true })); // Optional but good practice
58+
logConCgp('[Gemini] Input and change events dispatched.');
59+
60+
// Move cursor to the end after insertion (important for UX)
61+
MaxExtensionUtils.moveCursorToEnd(editor);
62+
};
63+
64+
// Insert the custom text
65+
insertTextIntoGeminiEditor(editorArea, customText);
66+
67+
// Auto-send logic
68+
if (globalMaxExtensionConfig.globalAutoSendEnabled && autoSend) {
69+
logConCgp('[Gemini] Auto-send enabled. Attempting to send.');
70+
71+
// Use a MutationObserver or interval to wait for the button to become enabled
72+
const attemptSend = (maxAttempts = 20) => {
73+
let attempts = 0;
74+
const intervalId = setInterval(() => {
75+
sendButton = locateSendButton(); // Re-check for the button
76+
if (sendButton) {
77+
clearInterval(intervalId);
78+
logConCgp('[Gemini] Send button found and enabled. Clicking.');
79+
MaxExtensionUtils.simulateClick(sendButton);
80+
} else if (++attempts >= maxAttempts) {
81+
clearInterval(intervalId);
82+
logConCgp('[Gemini] Send button did not become enabled after multiple attempts.');
83+
} else {
84+
logConCgp(`[Gemini] Send button not enabled yet. Attempt ${attempts}/${maxAttempts}`);
85+
}
86+
}, 100); // Check every 100ms
87+
};
88+
attemptSend(); // Start trying to send
89+
} else {
90+
logConCgp('[Gemini] Auto-send disabled or not requested for this button.');
91+
// Optional: Re-focus editor after insertion if not auto-sending
92+
editorArea.focus();
93+
MaxExtensionUtils.moveCursorToEnd(editorArea);
94+
}
95+
}
96+
97+
// Expose the function globally (if needed, depends on how buttons.js calls it)
98+
window.processGeminiCustomSendButtonClick = processGeminiCustomSendButtonClick;

buttons.js

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -6,11 +6,11 @@
66
This file is a dependency. Designed to host helper functions for init.js. Manages the creation and functionality of custom send buttons within the ChatGPT extension.
77
It provides utility functions to create buttons based on configuration and assigns keyboard shortcuts where applicable.
88
9-
After that, tje
9+
After that, tje
1010
1111
Usage:
1212
Ensure that dependencies are loaded before this script to utilize button functionalities.
13-
13+
1414
Depends on:
1515
utils.js - object containing all selectors and identifiers
1616
buttons-init.js - handles only some initializations.
@@ -132,15 +132,15 @@ function processCustomSendButtonClick(event, customText, autoSend) {
132132
case 'AIStudio':
133133
processAIStudioCustomSendButtonClick(event, customText, autoSend);
134134
break;
135-
case 'AIStudio':
136-
processAIStudioCustomSendButtonClick(event, customText, autoSend);
137-
break;
138135
case 'Grok':
139136
processGrokCustomSendButtonClick(event, customText, autoSend);
140137
break;
138+
case 'Gemini': // Added Gemini case
139+
processGeminiCustomSendButtonClick(event, customText, autoSend);
140+
break;
141141
default:
142142
logConCgp('[buttons] Unsupported site:', activeSite);
143143
}
144144
}
145145

146-
// #endregion
146+
// #endregion

manifest.json

Lines changed: 10 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -3,14 +3,14 @@
33
"name": "OneClickPrompts",
44
"description": "One Click Prompts for ChatGPT, Claude, Copilot, DeepSeek, Ai Studio",
55
"version": "0.0.2.0",
6-
6+
77
"icons": {
88
"16": "icon16.png",
99
"32": "icon32.png",
1010
"48": "icon48.png",
1111
"128": "icon128.png"
1212
},
13-
13+
1414
"action": {
1515
"default_popup": "popup.html",
1616
"default_icon": {
@@ -21,23 +21,23 @@
2121
},
2222
"default_title": "OneClickPrompts: Open User Interface"
2323
},
24-
24+
2525
"permissions": [
2626
"storage"
2727
],
28-
28+
2929
"background": {
3030
"service_worker": "config.js",
3131
"type": "module"
3232
},
33-
33+
3434
"web_accessible_resources": [
3535
{
3636
"resources": ["welcome.html"],
3737
"matches": ["<all_urls>"]
3838
}
3939
],
40-
40+
4141
"content_scripts": [
4242
{
4343
"matches": [
@@ -47,7 +47,8 @@
4747
"https://copilot.microsoft.com/*",
4848
"https://chat.deepseek.com/*",
4949
"https://aistudio.google.com/*",
50-
"https://grok.com/*"
50+
"https://grok.com/*",
51+
"https://gemini.google.com/*"
5152
],
5253
"js": [
5354
"log.js",
@@ -59,6 +60,7 @@
5960
"buttons-clicking-deepseek.js",
6061
"buttons-clicking-aistudio.js",
6162
"buttons-clicking-grok.js",
63+
"buttons-clicking-gemini.js",
6264
"buttons.js",
6365
"buttons-init.js",
6466
"interface.js",
@@ -72,4 +74,4 @@
7274
]
7375
}
7476
]
75-
}
77+
}

utils.js

Lines changed: 22 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,7 @@ window.MaxExtensionUtils = {
4545
editorDiv.focus();
4646

4747
// Escape HTML entities in the text
48-
const escapedText = text.replace(/</g, '&lt;').replace(/>/g, '&gt;');
48+
const escapedText = text.replace(/</g, '<').replace(/>/g, '>');
4949

5050
// Update the innerHTML directly
5151
editorDiv.innerHTML = `<p>${escapedText}</p>`;
@@ -91,7 +91,7 @@ window.MaxExtensionUtils = {
9191

9292
/**
9393
* InjectionTargetsOnWebsite
94-
*
94+
*
9595
* Centralizes all selectors and identifiers for different websites.
9696
* Currently implemented for ChatGPT and other sites. Other websites can be added as needed.
9797
*/
@@ -125,6 +125,9 @@ class InjectionTargetsOnWebsite {
125125
else if (currentHostname.includes('grok.com')) {
126126
return 'Grok';
127127
}
128+
else if (currentHostname.includes('gemini.google.com')) { // Added Gemini detection
129+
return 'Gemini';
130+
}
128131
// Add additional website detections here
129132
else {
130133
return 'Unknown';
@@ -271,12 +274,27 @@ class InjectionTargetsOnWebsite {
271274
'textarea.w-full.px-2.\\@\\[480px\\]\\/input\\:px-3.pt-5.mb-5.bg-transparent.focus\\:outline-none.text-primary.align-bottom'
272275
],
273276
buttonsContainerId: 'grok-custom-buttons-container'
274-
}
277+
},
278+
Gemini: {
279+
containers: [
280+
'input-container', // The main container holding input and disclaimer
281+
'main' // Fallback if input-container structure changes significantly
282+
],
283+
sendButtons: [
284+
'button.send-button[aria-label="Send message"]', // Primary send button
285+
'button[aria-label="Send message"][aria-disabled="false"]' // Explicitly enabled state
286+
],
287+
editors: [
288+
'div.ql-editor[contenteditable="true"]', // Quill editor div
289+
'rich-textarea div.ql-editor' // More specific path
290+
],
291+
buttonsContainerId: 'gemini-custom-buttons-container' // Unique ID
292+
}
275293
// TODO: Add selectors for other supported websites
276294
};
277295
return selectors[site] || {};
278296
}
279297
}
280298

281299
// Instantiate and expose the InjectionTargetsOnWebsite globally
282-
window.InjectionTargetsOnWebsite = new InjectionTargetsOnWebsite();
300+
window.InjectionTargetsOnWebsite = new InjectionTargetsOnWebsite();

0 commit comments

Comments
 (0)