@@ -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} ;
0 commit comments