@@ -14,114 +14,127 @@ function processChatGPTCustomSendButtonClick(event, customText, autoSend) {
1414 event . preventDefault ( ) ;
1515 logConCgp ( '[buttons] Custom send button was clicked.' ) ;
1616
17- const editorArea = document . querySelector ( window . InjectionTargetsOnWebsite . selectors . editors . find ( selector =>
18- document . querySelector ( selector )
19- ) ) ;
17+ // Locate the editor area using the provided selectors from the global InjectionTargetsOnWebsite object
18+ const editorArea = document . querySelector (
19+ window . InjectionTargetsOnWebsite . selectors . editors . find ( selector => document . querySelector ( selector ) )
20+ ) ;
2021
2122 if ( ! editorArea ) {
2223 logConCgp ( '[buttons] Editor area not found. Unable to proceed.' ) ;
2324 return ;
2425 }
2526
27+ // ----------------------------
28+ // Helper Functions (Modernized)
29+ // ----------------------------
30+
2631 /**
2732 * Determines if the editor is in its initial state (contains placeholder text).
2833 * @param {HTMLElement } element - The editor element.
2934 * @returns {boolean } - True if in initial state, false otherwise.
3035 */
31- function isEditorInInitialState ( element ) {
36+ const isEditorInInitialState = ( element ) => {
3237 const placeholderElement = element . querySelector ( 'p.placeholder' ) ;
3338 const isInitial = ! ! placeholderElement ;
3439 logConCgp ( '[buttons] Editor initial state check:' , isInitial ) ;
3540 return isInitial ;
36- }
41+ } ;
3742
38- // Helper function to create keyboard/input events
39- function createInputEvent ( type , char ) {
43+ /**
44+ * Creates an input or keyboard event for a given character.
45+ * @param {string } type - The event type ('keydown', 'input', or 'keyup').
46+ * @param {string } char - The character associated with the event.
47+ * @returns {Event } - The constructed event.
48+ */
49+ const createInputEvent = ( type , char ) => {
4050 const eventInit = {
4151 key : char ,
4252 code : `Key${ char . toUpperCase ( ) } ` ,
4353 bubbles : true ,
4454 composed : true ,
4555 cancelable : true
4656 } ;
47-
48- return type === 'input'
49- ? new InputEvent ( type , {
50- data : char ,
51- inputType : 'insertText' ,
52- bubbles : true ,
53- composed : true ,
54- cancelable : true
55- } )
57+
58+ return type === 'input'
59+ ? new InputEvent ( type , {
60+ data : char ,
61+ inputType : 'insertText' ,
62+ bubbles : true ,
63+ composed : true ,
64+ cancelable : true
65+ } )
5666 : new KeyboardEvent ( type , eventInit ) ;
57- }
67+ } ;
5868
5969 /**
6070 * Simulates typing text into a ProseMirror editor.
71+ * Uses modern for‑of loops and dispatches key events for each character.
6172 * @param {HTMLElement } editorElement - The ProseMirror editor element.
6273 * @param {string } text - The text to type into the editor.
6374 */
64- function simulateTypingIntoProseMirror ( editorElement , text ) {
75+ const simulateTypingIntoProseMirror = ( editorElement , text ) => {
6576 editorElement . focus ( ) ;
66- Array . from ( text ) . forEach ( char => {
67- [ 'keydown' , 'input' , 'keyup' ] . forEach ( eventType => {
68- const event = createInputEvent ( eventType , char ) ;
69- editorElement . dispatchEvent ( event ) ;
77+ const eventTypes = [ 'keydown' , 'input' , 'keyup' ] ;
78+ for ( const char of text ) {
79+ for ( const eventType of eventTypes ) {
80+ const evt = createInputEvent ( eventType , char ) ;
81+ editorElement . dispatchEvent ( evt ) ;
7082 if ( eventType === 'input' ) {
7183 // Modern approach to insert text
7284 const selection = window . getSelection ( ) ;
73- const range = selection . getRangeAt ( 0 ) ;
74- const textNode = document . createTextNode ( char ) ;
75- range . insertNode ( textNode ) ;
76- range . collapse ( false ) ;
77- selection . removeAllRanges ( ) ;
78- selection . addRange ( range ) ;
85+ if ( selection && selection . rangeCount > 0 ) {
86+ const range = selection . getRangeAt ( 0 ) ;
87+ const textNode = document . createTextNode ( char ) ;
88+ range . insertNode ( textNode ) ;
89+ range . collapse ( false ) ;
90+ selection . removeAllRanges ( ) ;
91+ selection . addRange ( range ) ;
92+ }
7993 }
80- } ) ;
81- } ) ;
82- }
94+ }
95+ }
96+ } ;
8397
8498 /**
8599 * Locates all send buttons based on the provided selectors.
86100 * @returns {HTMLElement[] } Array of found send button elements.
87101 */
88- function locateSendButtons ( ) {
102+ const locateSendButtons = ( ) => {
89103 return [ ...new Set (
90104 window . InjectionTargetsOnWebsite . selectors . sendButtons
91105 . flatMap ( selector => [ ...document . querySelectorAll ( selector ) ] )
92106 ) ] ;
93- }
107+ } ;
94108
95- // Locate send buttons initially
109+ // Initial retrieval of send buttons.
96110 let originalSendButtons = locateSendButtons ( ) ;
97111
98112 /**
99- * Handles the send buttons by inserting text and initiating auto-send if enabled.
100- * Operates on only the first send button to prevent duplicate sends.
113+ * Handles the send buttons by initiating auto-send if enabled.
114+ * Operates only on the first send button to prevent duplicate sends.
101115 * @param {HTMLElement[] } sendButtons - Array of send button elements.
102116 */
103- function handleSendButtons ( sendButtons ) {
117+ const handleSendButtons = ( sendButtons ) => {
104118 logConCgp ( '[buttons] handleSendButtons called.' ) ;
105119 if ( ! sendButtons . length ) {
106120 logConCgp ( '[buttons] Send buttons are not available to handle.' ) ;
107121 return ;
108122 }
109-
110123 logConCgp ( '[buttons] Send buttons are available. Proceeding with sending message.' ) ;
111-
124+
112125 if ( globalMaxExtensionConfig . globalAutoSendEnabled && autoSend ) {
113126 logConCgp ( '[buttons] Auto-send is enabled. Starting auto-send process.' ) ;
114127 startAutoSend ( [ sendButtons [ 0 ] ] , editorArea ) ;
115128 }
116- }
129+ } ;
117130
118131 /**
119132 * Starts the auto-send interval to automatically click send buttons until the editor is empty.
120133 * Ensures only one interval runs at a time and clicks only the first send button.
121134 * @param {HTMLElement[] } sendButtons - Array of send button elements.
122135 * @param {HTMLElement } editor - The editor area element.
123136 */
124- function startAutoSend ( sendButtons , editor ) {
137+ const startAutoSend = ( sendButtons , editor ) => {
125138 logConCgp ( '[auto-send] startAutoSend called.' ) ;
126139
127140 // Prevent multiple auto-send intervals from running simultaneously
@@ -131,7 +144,6 @@ function processChatGPTCustomSendButtonClick(event, customText, autoSend) {
131144 }
132145
133146 logConCgp ( '[auto-send] Starting auto-send interval to click send button every 100ms.' ) ;
134-
135147 window . autoSendInterval = setInterval ( ( ) => {
136148 const currentText = editor . innerText . trim ( ) ;
137149 logConCgp ( '[auto-send] Current text in editor:' , currentText ) ;
@@ -146,7 +158,6 @@ function processChatGPTCustomSendButtonClick(event, customText, autoSend) {
146158
147159 // Attempt to locate send buttons again in case they have changed
148160 const updatedSendButtons = locateSendButtons ( ) ;
149-
150161 if ( updatedSendButtons . length === 0 ) {
151162 logConCgp ( '[auto-send] Send button not found during auto-send. Stopping auto-send interval.' ) ;
152163 clearInterval ( window . autoSendInterval ) ;
@@ -160,75 +171,69 @@ function processChatGPTCustomSendButtonClick(event, customText, autoSend) {
160171 logConCgp ( '[auto-send] Clicking send button:' , sendButton ) ;
161172 MaxExtensionUtils . simulateClick ( sendButton ) ;
162173 logConCgp ( '[auto-send] Send button clicked successfully.' ) ;
163-
164- // After a successful click, assume the message is sent and stop auto-send
165174 clearInterval ( window . autoSendInterval ) ;
166175 window . autoSendInterval = null ;
167176 logConCgp ( '[auto-send] Auto-send interval stopped after successful send.' ) ;
168177 } else {
169178 logConCgp ( '[auto-send] Send button not found during auto-send.' ) ;
170179 }
171180 } , 100 ) ; // Interval set to 100 milliseconds
172- }
181+ } ;
182+
183+ /**
184+ * Waits for send buttons to appear in the DOM using a promise-based approach.
185+ * This modern helper replaces the previous MutationObserver duplication.
186+ * @param {number } timeout - Maximum time in milliseconds to wait for the buttons.
187+ * @returns {Promise<HTMLElement[]> } Resolves with the send buttons if found.
188+ */
189+ const waitForSendButtons = ( timeout = 5000 ) => {
190+ return new Promise ( ( resolve , reject ) => {
191+ const buttons = locateSendButtons ( ) ;
192+ if ( buttons . length > 0 ) {
193+ return resolve ( buttons ) ;
194+ }
195+ const observer = new MutationObserver ( ( mutations , obs ) => {
196+ const buttonsNow = locateSendButtons ( ) ;
197+ if ( buttonsNow . length > 0 ) {
198+ obs . disconnect ( ) ;
199+ resolve ( buttonsNow ) ;
200+ }
201+ } ) ;
202+ observer . observe ( document . body , { childList : true , subtree : true } ) ;
203+ setTimeout ( ( ) => {
204+ observer . disconnect ( ) ;
205+ reject ( new Error ( 'Timeout waiting for send buttons' ) ) ;
206+ } , timeout ) ;
207+ } ) ;
208+ } ;
173209
174210 /**
175211 * Handles the entire process of inserting text and sending the message.
176212 * This includes state detection, text insertion, and send button handling.
177213 */
178- function handleMessageInsertion ( ) {
214+ const handleMessageInsertion = ( ) => {
179215 logConCgp ( '[buttons] handleMessageInsertion called.' ) ;
180216 const initialState = isEditorInInitialState ( editorArea ) ;
181217
182218 if ( initialState ) {
183219 logConCgp ( '[buttons] Editor is in initial state. Simulating typing.' ) ;
184-
185220 // Simulate typing to activate the editor and insert text
186221 simulateTypingIntoProseMirror ( editorArea , customText ) ;
187222 logConCgp ( '[buttons] Custom text typed into editor.' ) ;
188223
189- // Wait for the send button to appear
224+ // Use the promise-based helper to wait for send buttons instead of duplicating MutationObserver logic
190225 setTimeout ( ( ) => {
191- // Proceed to handle send buttons
192- originalSendButtons = locateSendButtons ( ) ;
193-
194- if ( originalSendButtons . length === 0 ) {
195- // If send buttons are still not found, set up a MutationObserver
196- logConCgp ( '[buttons] Send buttons not found after typing. Setting up MutationObserver.' ) ;
197- const observer = new MutationObserver ( ( mutations , obs ) => {
198- originalSendButtons = locateSendButtons ( ) ;
199- if ( originalSendButtons . length > 0 ) {
200- handleSendButtons ( originalSendButtons ) ;
201- obs . disconnect ( ) ;
202- logConCgp ( '[buttons] Send buttons detected and observer disconnected.' ) ;
203- } else {
204- logConCgp ( '[buttons] MutationObserver detected changes, but send buttons still not found.' ) ;
205- }
226+ waitForSendButtons ( 5000 )
227+ . then ( ( buttons ) => {
228+ originalSendButtons = buttons ;
229+ handleSendButtons ( originalSendButtons ) ;
230+ } )
231+ . catch ( ( error ) => {
232+ logConCgp ( '[buttons] ' + error . message ) ;
206233 } ) ;
207-
208- // Start observing the DOM for changes to detect send buttons
209- observer . observe ( document . body , {
210- childList : true ,
211- subtree : true
212- } ) ;
213-
214- // Set a timeout to disconnect the observer after a reasonable time
215- setTimeout ( ( ) => {
216- if ( window . autoSendInterval ) {
217- // If auto-send is already running, do not disconnect
218- logConCgp ( '[buttons] MutationObserver timeout reached, but auto-send is running. Observer remains.' ) ;
219- return ;
220- }
221- observer . disconnect ( ) ;
222- logConCgp ( '[buttons] MutationObserver disconnected after timeout.' ) ;
223- } , 5000 ) ; // 5 seconds timeout
224- } else {
225- // If send buttons are found after insertion, handle them
226- handleSendButtons ( originalSendButtons ) ;
227- }
228234 } , 500 ) ; // Delay to allow the editor to update
229235 } else {
230236 logConCgp ( '[buttons] Editor is already in active state. Proceeding with existing logic.' ) ;
231-
232237 // Retrieve existing text in the editor
233238 const existingText = editorArea . innerText ;
234239 logConCgp ( '[buttons] Current text in editor:' , existingText ) ;
@@ -237,18 +242,19 @@ function processChatGPTCustomSendButtonClick(event, customText, autoSend) {
237242 const newText = `${ existingText } ${ customText } ` ;
238243 logConCgp ( '[buttons] Combined text to be inserted:' , newText ) ;
239244
240- // Insert the new text into the editor
245+ // Insert the new text into the editor using the external utility methods
241246 MaxExtensionUtils . insertTextIntoEditor ( editorArea , newText ) ;
242-
243247 // Move cursor to the end after insertion
244248 MaxExtensionUtils . moveCursorToEnd ( editorArea ) ;
245249
246- // Proceed to handle send buttons
250+ // Proceed to handle send buttons immediately
247251 handleSendButtons ( originalSendButtons ) ;
248252 }
249- }
253+ } ;
250254
255+ // ----------------------------
251256 // Start the message insertion and sending process
257+ // ----------------------------
252258 handleMessageInsertion ( ) ;
253259}
254260
0 commit comments