Gemini Enterprise HD Thumbnails & Auto 3 Pro

Automatically switch to Gemini 3 Pro model and force HD quality thumbnails in Gemini Enterprise

Size

13.7 KB

Version

1.1.1

Created

Jan 16, 2026

Updated

18 days ago

1// ==UserScript==
2// @name		Gemini Enterprise HD Thumbnails & Auto 3 Pro
3// @namespace		http://tampermonkey.net/
4// @version		1.1.1
5// @description		Automatically switch to Gemini 3 Pro model and force HD quality thumbnails in Gemini Enterprise
6// @author		schweigen
7// @match		https://business.gemini.google/*
8// @run-at		document-end
9// @grant		none
10// @license		MIT
11// @downloadURL		https://update.greasyfork.org/scripts/557022/Gemini%20Enterprise%20-%20Auto%20Gemini%203%20Pro.user.js
12// @updateURL		https://update.greasyfork.org/scripts/557022/Gemini%20Enterprise%20-%20Auto%20Gemini%203%20Pro.meta.js
13// ==/UserScript==
14(function () {
15    'use strict';
16
17    const TARGET_LABEL = '3 Pro';
18    const LOG_PREFIX = '[GeminiAuto3Pro]';
19
20    function log() {
21        if (typeof console === 'undefined' || !console.log) {
22            return;
23        }
24        var args = Array.prototype.slice.call(arguments);
25        args.unshift(LOG_PREFIX);
26        console.log.apply(console, args);
27    }
28
29    function deepQuerySelector(selector) {
30        function search(root) {
31            if (!root || !root.querySelector) {
32                return null;
33            }
34            var found = root.querySelector(selector);
35            if (found) {
36                return found;
37            }
38            var elements = root.querySelectorAll('*');
39            for (var i = 0; i < elements.length; i++) {
40                var el = elements[i];
41                if (el.shadowRoot) {
42                    var fromShadow = search(el.shadowRoot);
43                    if (fromShadow) {
44                        return fromShadow;
45                    }
46                }
47            }
48            return null;
49        }
50        return search(document);
51    }
52
53    function deepQuerySelectorAll(selector) {
54        var collected = [];
55        function search(root) {
56            if (!root || !root.querySelectorAll) {
57                return;
58            }
59            var matches = root.querySelectorAll(selector);
60            for (var i = 0; i < matches.length; i++) {
61                collected.push(matches[i]);
62            }
63            var elements = root.querySelectorAll('*');
64            for (var j = 0; j < elements.length; j++) {
65                var el = elements[j];
66                if (el.shadowRoot) {
67                    search(el.shadowRoot);
68                }
69            }
70        }
71        search(document);
72        return collected;
73    }
74
75    function waitForElement(selector, timeoutMs) {
76        return new Promise(function (resolve, reject) {
77            var start = Date.now();
78            log('waitForElement: start polling for', selector);
79
80            function check() {
81                var found = deepQuerySelector(selector);
82                if (found) {
83                    log('waitForElement: found:', selector);
84                    resolve(found);
85                    return;
86                }
87                var elapsed = Date.now() - start;
88                if (elapsed >= timeoutMs) {
89                    log('waitForElement: timeout for', selector);
90                    reject(new Error('Timeout waiting for selector: ' + selector));
91                    return;
92                }
93                setTimeout(check, 400);
94            }
95
96            check();
97        });
98    }
99
100    function findModelSelectorHost() {
101        const candidates = deepQuerySelectorAll(
102            'md-text-button.action-model-selector#model-selector-menu-anchor'
103        );
104        if (!candidates.length) {
105            log('findModelSelectorHost: no candidates');
106            return null;
107        }
108
109        // Prefer the one that actually looks like the main model selector
110        const preferred = candidates.find(function (el) {
111            const text = (el.textContent || '').toLowerCase();
112            return text.includes('auto') || text.includes('gemini');
113        });
114
115        const host = preferred || candidates[candidates.length - 1];
116        log(
117            'findModelSelectorHost: picked host, text=',
118            (host.textContent || '').trim()
119        );
120        return host;
121    }
122
123    function findModelSelectorButton() {
124        const host = findModelSelectorHost();
125        if (!host) {
126            log('findModelSelectorButton: host not found');
127            return null;
128        }
129        const shadowButton = host.shadowRoot && host.shadowRoot.querySelector('button');
130        const button = shadowButton || host;
131        log('findModelSelectorButton: using element:', button.tagName);
132        return button;
133    }
134
135    function isAlreadyOnTarget() {
136        const host = findModelSelectorHost();
137        if (!host) {
138            return false;
139        }
140        const labelText = (host.textContent || '').trim();
141        log('isAlreadyOnTarget: current label =', labelText);
142        return labelText.includes(TARGET_LABEL);
143    }
144
145    function openModelMenuIfNeeded() {
146        const menuElement = deepQuerySelector('md-menu.model-selector-menu');
147        if (menuElement) {
148            const isHidden = menuElement.getAttribute('aria-hidden') === 'true';
149            const hasOpenAttribute = menuElement.hasAttribute('open');
150            if (!isHidden && hasOpenAttribute) {
151                log('openModelMenuIfNeeded: menu already open');
152                return true;
153            }
154        }
155
156        const buttonElement = findModelSelectorButton();
157        if (!buttonElement) {
158            log('openModelMenuIfNeeded: button not found');
159            return false;
160        }
161
162        log('openModelMenuIfNeeded: clicking button to open menu');
163        buttonElement.click();
164        return true;
165    }
166
167    function clickGemini3Pro() {
168        const menuElement = deepQuerySelector('md-menu.model-selector-menu');
169        if (!menuElement) {
170            log('clickGemini3Pro: menu element not found');
171            return false;
172        }
173
174        const menuItems = Array.from(menuElement.querySelectorAll('md-menu-item'));
175        if (!menuItems.length) {
176            log('clickGemini3Pro: no menu items found');
177            return false;
178        }
179
180        const targetItem = menuItems.find(function (itemElement) {
181            const text = (itemElement.textContent || '').trim();
182            log('clickGemini3Pro: checking menu item text =', text);
183            return text.includes(TARGET_LABEL);
184        });
185
186        if (!targetItem) {
187            log('clickGemini3Pro: target item not found for label', TARGET_LABEL);
188            return false;
189        }
190
191        // Prefer clicking the actual <li id="item"> inside the shadow DOM if present
192        var interactiveElement = targetItem;
193        if (targetItem.shadowRoot) {
194            var li = targetItem.shadowRoot.querySelector('#item');
195            if (li) {
196                interactiveElement = li;
197            }
198        }
199
200        log('clickGemini3Pro: clicking target item');
201        interactiveElement.click();
202        return true;
203    }
204
205    function trySelectGemini3ProOnce() {
206        if (isAlreadyOnTarget()) {
207            log('trySelectGemini3ProOnce: already on target');
208            return true;
209        }
210
211        const opened = openModelMenuIfNeeded();
212        if (!opened) {
213            log('trySelectGemini3ProOnce: could not open menu');
214            return false;
215        }
216
217        setTimeout(function () {
218            log('trySelectGemini3ProOnce: attempting to click Gemini 3 Pro');
219            waitForElement('md-menu.model-selector-menu', 10000)
220                .then(function () {
221                    clickGemini3Pro();
222                })
223                .catch(function (err) {
224                    log('trySelectGemini3ProOnce: menu did not appear in time', err && err.message);
225                });
226        }, 350);
227
228        return true;
229    }
230
231    function bootstrapSelectionWithObserver() {
232        log('bootstrapSelectionWithObserver: start');
233        waitForElement('md-text-button.action-model-selector#model-selector-menu-anchor', 30000)
234            .then(function () {
235                setTimeout(function () {
236                    trySelectGemini3ProOnce();
237                }, 50);
238            })
239            .catch(function () {
240                log('bootstrapSelectionWithObserver: waitForElement failed, falling back to single attempt');
241                trySelectGemini3ProOnce();
242            });
243    }
244
245    function removeDisclaimersOnce() {
246        var disclaimers = deepQuerySelectorAll('div.disclaimer');
247        if (!disclaimers || !disclaimers.length) {
248            return false;
249        }
250        for (var i = 0; i < disclaimers.length; i++) {
251            var el = disclaimers[i];
252            try {
253                log(
254                    'removeDisclaimersOnce: removing disclaimer with text =',
255                    (el.textContent || '').trim()
256                );
257            } catch {
258                // ignore logging failures
259            }
260            if (el && el.parentNode) {
261                el.parentNode.removeChild(el);
262            } else if (el && el.remove) {
263                el.remove();
264            }
265        }
266        return true;
267    }
268
269    function bootstrapDisclaimerRemoval() {
270        log('bootstrapDisclaimerRemoval: start');
271        var attempts = 0;
272        var maxAttempts = 30;
273
274        function tick() {
275            attempts++;
276            var removed = removeDisclaimersOnce();
277            if (removed || attempts >= maxAttempts) {
278                if (removed) {
279                    log('bootstrapDisclaimerRemoval: disclaimer(s) removed');
280                } else {
281                    log('bootstrapDisclaimerRemoval: no disclaimers found after retries');
282                }
283                return;
284            }
285            setTimeout(tick, 1000);
286        }
287
288        tick();
289    }
290
291    function forceHDThumbnails() {
292        log('forceHDThumbnails: start');
293        
294        function replaceWithHD(img) {
295            if (!img || !img.src) {
296                return false;
297            }
298            
299            var originalSrc = img.src;
300            var hdSrc = originalSrc;
301            
302            // Replace common low-quality thumbnail patterns with HD versions
303            // Pattern 1: =s followed by dimensions (e.g., =s48, =s96)
304            if (hdSrc.includes('=s')) {
305                hdSrc = hdSrc.replace(/=s\d+(-c)?/g, '=s0');
306                log('forceHDThumbnails: replaced =s pattern:', originalSrc, '->', hdSrc);
307            }
308            
309            // Pattern 2: =w followed by dimensions (e.g., =w48, =w96)
310            if (hdSrc.includes('=w')) {
311                hdSrc = hdSrc.replace(/=w\d+(-h\d+)?(-c)?/g, '=s0');
312                log('forceHDThumbnails: replaced =w pattern:', originalSrc, '->', hdSrc);
313            }
314            
315            // Pattern 3: -rw or -rh followed by dimensions
316            if (hdSrc.includes('-rw') || hdSrc.includes('-rh')) {
317                hdSrc = hdSrc.replace(/-r[wh]\d+/g, '=s0');
318                log('forceHDThumbnails: replaced -rw/-rh pattern:', originalSrc, '->', hdSrc);
319            }
320            
321            if (hdSrc !== originalSrc) {
322                img.src = hdSrc;
323                img.srcset = '';
324                img.setAttribute('data-hd-forced', 'true');
325                return true;
326            }
327            
328            return false;
329        }
330        
331        function processAllImages() {
332            var images = document.querySelectorAll('img:not([data-hd-forced])');
333            var count = 0;
334            
335            for (var i = 0; i < images.length; i++) {
336                if (replaceWithHD(images[i])) {
337                    count++;
338                }
339            }
340            
341            if (count > 0) {
342                log('forceHDThumbnails: processed', count, 'images');
343            }
344            
345            return count;
346        }
347        
348        // Initial processing
349        processAllImages();
350        
351        // Watch for new images being added
352        var observer = new MutationObserver(function(mutations) {
353            var hasNewImages = false;
354            
355            for (var i = 0; i < mutations.length; i++) {
356                var mutation = mutations[i];
357                
358                if (mutation.type === 'childList' && mutation.addedNodes.length > 0) {
359                    for (var j = 0; j < mutation.addedNodes.length; j++) {
360                        var node = mutation.addedNodes[j];
361                        
362                        if (node.nodeType === 1) { // Element node
363                            if (node.tagName === 'IMG') {
364                                hasNewImages = true;
365                                break;
366                            } else if (node.querySelectorAll) {
367                                var imgs = node.querySelectorAll('img');
368                                if (imgs.length > 0) {
369                                    hasNewImages = true;
370                                    break;
371                                }
372                            }
373                        }
374                    }
375                }
376                
377                if (hasNewImages) break;
378            }
379            
380            if (hasNewImages) {
381                processAllImages();
382            }
383        });
384        
385        observer.observe(document.body, {
386            childList: true,
387            subtree: true
388        });
389        
390        log('forceHDThumbnails: observer installed');
391    }
392
393    log('script loaded');
394    if (typeof window !== 'undefined') {
395        try {
396            window.__GeminiEnterpriseAutoGemini3Pro = true;
397        } catch {
398            // ignore
399        }
400    }
401
402    if (document.readyState === 'complete' || document.readyState === 'interactive') {
403        bootstrapSelectionWithObserver();
404        bootstrapDisclaimerRemoval();
405        forceHDThumbnails();
406    } else {
407        window.addEventListener('DOMContentLoaded', function () {
408            bootstrapSelectionWithObserver();
409            bootstrapDisclaimerRemoval();
410            forceHDThumbnails();
411        });
412    }
413})();
Gemini Enterprise HD Thumbnails & Auto 3 Pro | Robomonkey