Gemini-UI-Optimizer

Gemini对话页面UI优化:1.更宽排版展示AI回答 2.输入框压扁释放竖向空间 3.AI回答红绿配色

Чтобы установить этот скрипт, вы сначала должны установить расширение браузера, например Tampermonkey, Greasemonkey или Violentmonkey.

Для установки этого скрипта вам необходимо установить расширение, такое как Tampermonkey.

Чтобы установить этот скрипт, вы сначала должны установить расширение браузера, например Tampermonkey или Violentmonkey.

Чтобы установить этот скрипт, вы сначала должны установить расширение браузера, например Tampermonkey или Userscripts.

Чтобы установить этот скрипт, сначала вы должны установить расширение браузера, например Tampermonkey.

Чтобы установить этот скрипт, вы должны установить расширение — менеджер скриптов.

(у меня уже есть менеджер скриптов, дайте мне установить скрипт!)

Чтобы установить этот стиль, сначала вы должны установить расширение браузера, например Stylus.

Чтобы установить этот стиль, сначала вы должны установить расширение браузера, например Stylus.

Чтобы установить этот стиль, сначала вы должны установить расширение браузера, например Stylus.

Чтобы установить этот стиль, сначала вы должны установить расширение — менеджер стилей.

Чтобы установить этот стиль, сначала вы должны установить расширение — менеджер стилей.

Чтобы установить этот стиль, сначала вы должны установить расширение — менеджер стилей.

(у меня уже есть менеджер стилей, дайте мне установить скрипт!)

// ==UserScript==
// @name        Gemini-UI-Optimizer
// @namespace   https://example.com/efficient-lazy-panda
// @match       *://claude.ai/*
// @match       *://chatgpt.com/*
// @match       *://gemini.google.com/*
// @version     1.4
// @author      Efficient Lazy Panda
// @license     MIT
// @description  Gemini对话页面UI优化:1.更宽排版展示AI回答 2.输入框压扁释放竖向空间 3.AI回答红绿配色
// ==/UserScript==

(function () {
  'use strict';

  // Detect which platform we're on
  const isGemini = window.location.hostname.includes('gemini.google.com');
  const isClaude = window.location.hostname.includes('claude.ai');
  const isChatGPT = window.location.hostname.includes('chatgpt.com');

  // Create a style element
  const style = document.createElement('style');

  // Common font styles for all platforms
  const commonFontStyles = `
        /* Common normalized font styles */
        body {
            font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Oxygen-Sans, Ubuntu, Cantarell, "Helvetica Neue", sans-serif !important;
        }
    `;

  // Set platform-specific CSS
  if (isGemini) {
    style.textContent = commonFontStyles + `
            /* Gemini wide screen CSS */
            .chat-window,
            .chat-container,
            .conversation-container,
            .gemini-conversation-container {
                max-width: 95% !important;
                width: 95% !important;
            }

            .input-area-container,
            textarea,
            .prompt-textarea,
            .prompt-container {
                max-width: 95% !important;
                width: 95% !important;
            }

            textarea {
                width: 100% !important;
            }

            .max-w-3xl,
            .max-w-4xl,
            .max-w-screen-md {
                max-width: 95% !important;
            }

            .message-content,
            .user-message,
            .model-response {
                width: 100% !important;
                max-width: 100% !important;
            }

            .pre-fullscreen {
                height: auto !important;
            }

            .input-buttons-wrapper-top {
                right: 8px !important;
            }

            /* ==========================================================
             * Gemini Input Area Compression (Single Row)
             * ========================================================== */

            /* Make the main fieldset a flex row */
            .input-area-container fieldset,
            /* =========================================================
               🚀 GEMINI INPUT AREA COMPRESSION - PERFECT FIX 🚀
               Based on Grid override: .text-input-field -> [ .leading-actions-wrapper, .text-input-field_textarea-wrapper, .trailing-actions-wrapper ]
               ========================================================= */

            /* 1. Reset the grid container to a horizontal flex row */
            .text-input-field {
                display: flex !important;
                flex-direction: row !important;
                align-items: flex-end !important; /* Align to bottom so buttons don't float if text gets tall */
                flex-wrap: nowrap !important;
                padding: 4px 8px !important;
                min-height: 48px !important;
            }

            /* 2. Left Side (Plus button, Tool drawer) */
            .text-input-field > .leading-actions-wrapper {
                order: 1 !important;
                position: static !important;
                display: flex !important;
                flex-direction: row !important;
                align-items: center !important;
                height: auto !important;
                margin-bottom: 4px !important; /* Align with bottom */
                margin-right: 4px !important;
            }

            /* 3. Middle (The Input Text Box Wrapper) */
            .text-input-field > .text-input-field_textarea-wrapper {
                order: 2 !important;
                position: static !important;
                flex: 1 1 auto !important; /* Grow to take remaining space */
                width: auto !important;
                display: flex !important;
                align-items: stretch !important;
                margin: 0 !important;
                padding: 0 !important;
            }

            /* The actual editor textarea */
            rich-textarea .ql-editor {
                flex: 1 1 auto !important;
                min-height: 28px !important;
                max-height: 250px !important; /* Auto-grow limit */
                padding: 8px 12px 8px 105px !important; /* Shift cursor right by 95px to avoid overlap */
                margin: 0 !important;
                overflow-y: auto !important;
            }

            /* Hide the placeholder text ("问问 Gemini") */
            rich-textarea .ql-editor.ql-blank::before,
            rich-textarea .ql-editor::before {
                display: none !important;
                content: none !important;
                color: transparent !important;
            }

            /* Hide the text label "工具" (Tools) while keeping the icon */
            .toolbox-drawer-button .mdc-button__label span,
            .toolbox-drawer-button span:not(.mat-icon):not(.mat-mdc-button-persistent-ripple):not(.mat-mdc-button-touch-target) {
                display: none !important;
            }

            /* 4. Right Side (Pro button, Send button, Mic) */
            .text-input-field > .trailing-actions-wrapper {
                order: 3 !important;
                position: static !important;
                display: flex !important;
                flex-direction: row !important;
                align-items: center !important;
                height: auto !important;
                margin-bottom: 4px !important;
                margin-left: 4px !important;
            }

            /* Hide disclaimer and any bottom spacer */
            .footer-privacy-notice-link,
            div:has(> a.footer-privacy-notice-link),
            .disclaimer-container,
            .footer-text,
            hallucination-disclaimer {
                display: none !important;
            }

            /* Reduce bottom padding of chat history since input area is much smaller */
            .conversation-container,
            .chat-history,
            [class*="response-container"] {
                padding-bottom: 24px !important;
            }

            /* ==========================================================
             * Hide Top Bar to reclaim vertical space
             * ========================================================== */

            /* Google One Bar outer container + buffer spacer */
            div.boqOnegoogleliteOgbOneGoogleBar,
            div.desktop-ogb-buffer,
            #gb {
                display: none !important;
                height: 0 !important;
                margin: 0 !important;
                padding: 0 !important;
                overflow: hidden !important;
            }

            /* Gemini app top bar (conversation title, upgrade button, etc.) */
            .top-bar-actions,
            .conversation-title-container,
            header.gmat-headline-4,
            div[class*="top-bar"] {
                display: none !important;
                height: 0 !important;
                overflow: hidden !important;
            }

            /* Hide the original Gemini text */
            span[data-test-id="bard-text"] {
                display: none !important;
            }

            /* Style for the relocated title */
            #relocated-conv-title {
                color: #aaa !important;
                font-size: 14px !important;
                font-weight: normal !important;
                white-space: nowrap !important;
                overflow: hidden !important;
                text-overflow: ellipsis !important;
                max-width: 250px !important;
            }

            /* ==========================================================
             * Heading colors in Gemini responses
             * ========================================================== */
            .response-container h2,
            .model-response-text h2,
            message-content h2 {
                color: rgb(158, 8, 8) !important;
            }

            .response-container h3,
            .model-response-text h3,
            message-content h3 {
                color: rgb(158, 8, 8) !important;
            }

            .response-container ol li::marker,
            .model-response-text ol li::marker,
            message-content ol li::marker,
            .response-container ul li::marker,
            .model-response-text ul li::marker,
            message-content ul li::marker {
                color: rgb(0, 128, 0) !important;
            }

            .response-container hr,
            .model-response-text hr,
            message-content hr {
                border-color: rgb(158, 8, 8) !important;
            }

            .response-container b,
            .model-response-text b,
            message-content b {
                color: rgb(0, 128, 0) !important;
            }

            /* Reset all text to normal weight (no bold) */
            .response-container *,
            .model-response-text *,
            message-content * {
                font-weight: normal !important;
            }
        `;
  } else if (isClaude) {
    style.textContent = commonFontStyles + `
            /* Claude wide screen CSS */
            .max-w-screen-md, .max-w-3xl, .max-w-4xl {
                max-width: 95% !important;
            }

            .w-full.max-w-3xl, .w-full.max-w-4xl {
                max-width: 95% !important;
                width: 95% !important;
            }

            .w-full.max-w-3xl textarea {
                width: 100% !important;
            }

            .mx-auto {
                max-width: 95% !important;
            }

            [data-message-author-role] {
                width: 100% !important;
            }

            .absolute.right-0 {
                right: 10px !important;
            }

            /* Claude specific font fixes */
            p, h1, h2, h3, h4, h5, h6, span, div, textarea, input, button {
                font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Oxygen-Sans, Ubuntu, Cantarell, "Helvetica Neue", sans-serif !important;
                font-weight: 400 !important;
            }

            /* Fix for code blocks */
            pre, code, .font-mono {
                font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace !important;
            }

            /* Fix for chat messages */
            [data-message-author-role] p {
                font-size: 16px !important;
                line-height: 1.5 !important;
                letter-spacing: normal !important;
            }

            /* Remove serif from headings */
            h1, h2, h3, h4, h5, h6 {
                font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Oxygen-Sans, Ubuntu, Cantarell, "Helvetica Neue", sans-serif !important;
            }
        `;
  } else if (isChatGPT) {
    style.textContent = commonFontStyles + `
            /* ChatGPT wide screen CSS - Updated for 2024/2025 */

            /* Main container selectors */
            .mx-auto,
            .max-w-3xl,
            .max-w-4xl,
            .max-w-2xl,
            .max-w-screen-md {
                max-width: 95% !important;
                width: 95% !important;
            }

            /* Chat container */
            main {
                max-width: 95% !important;
                width: 95% !important;
            }

            /* Conversation container */
            [role="main"],
            .conversation,
            .chat-container {
                max-width: 95% !important;
                width: 95% !important;
            }

            /* Message containers */
            .group,
            .text-base,
            .gap-4,
            .md\\:gap-6 {
                max-width: 100% !important;
                width: 100% !important;
            }

            /* Input area */
            .stretch,
            .flex-1,
            .textarea,
            textarea {
                width: 100% !important;
                max-width: 100% !important;
            }

            /* Form container */
            form {
                width: 100% !important;
                max-width: 95% !important;
            }

            /* Chat message wrapper */
            .w-full {
                width: 100% !important;
                max-width: 100% !important;
            }

            /* Specific class combinations that ChatGPT uses */
            .flex.flex-col.pb-2,
            .flex.w-full.flex-col,
            .relative.flex.w-full.flex-col {
                width: 100% !important;
                max-width: 100% !important;
            }

            /* Override any fixed width containers */
            [class*="max-w-"] {
                max-width: 95% !important;
            }

            /* Ensure message content uses full width */
            .prose {
                max-width: 100% !important;
            }

            /* Input field specific styling */
            [data-id] textarea,
            [contenteditable="true"] {
                width: 100% !important;
                max-width: 100% !important;
            }

            /* Button container adjustments */
            .absolute.bottom-0.right-0,
            .absolute.right-0 {
                right: 8px !important;
            }

            /* Responsive adjustments */
            @media (min-width: 768px) {
                .md\\:max-w-2xl,
                .md\\:max-w-3xl,
                .md\\:max-w-4xl {
                    max-width: 95% !important;
                }
            }

            @media (min-width: 1024px) {
                .lg\\:max-w-2xl,
                .lg\\:max-w-3xl,
                .lg\\:max-w-4xl {
                    max-width: 95% !important;
                }
            }

            @media (min-width: 1280px) {
                .xl\\:max-w-3xl,
                .xl\\:max-w-4xl {
                    max-width: 95% !important;
                }
            }
        `;
  }

  // Append the style element to the document head
  document.head.appendChild(style);

  // Function to apply wide mode to inline styles (especially for Gemini)
  function applyWideModeToInlineStyles() {
    if (!isGemini) return; // Only needed for Gemini

    // Find elements with inline styles containing max-width
    const elements = document.querySelectorAll('[style*="max-width"]');

    elements.forEach(el => {
      // Skip elements that might be side panels or navigation
      if (el.classList.contains('side-panel') ||
        el.classList.contains('navigation-panel')) {
        return;
      }

      // Set max-width to 95%
      el.style.maxWidth = '95%';
    });
  }

  // Function to ensure Gemini's nested toolbar divs are 'unwrapped'
  function optimizeGeminiInputLayout() {
    if (!isGemini) return;
    const fieldsets = document.querySelectorAll('fieldset');
    for (const fs of fieldsets) {
      const bottomBars = fs.querySelectorAll(
        '.input-area-bottom-content, .input-buttons-container'
      );
      for (const bar of bottomBars) {
        bar.style.display = 'contents';
      }
    }
  }

  // Function to relocate the conversation title to the Gemini logo area
  function relocateConversationTitle() {
    if (!isGemini) return;

    // Find the conversation title using exact data-test-id
    const titleEl = document.querySelector('span[data-test-id="conversation-title"]');
    if (!titleEl) return;
    const titleText = titleEl.textContent.trim();
    if (!titleText) return;

    // Find the Gemini logo text using exact data-test-id
    const geminiText = document.querySelector('span[data-test-id="bard-text"]');
    if (!geminiText) return;

    // Check if we already injected the title
    let relocated = document.getElementById('relocated-conv-title');
    if (relocated) {
      if (relocated.textContent !== titleText) {
        relocated.textContent = titleText;
      }
      return;
    }

    // Create and inject the title element right in place of the Gemini text
    relocated = document.createElement('span');
    relocated.id = 'relocated-conv-title';
    relocated.textContent = titleText;
    geminiText.parentElement.insertBefore(relocated, geminiText.nextSibling);
  }

  // ChatGPT specific dynamic width application
  function applyChatGPTWideMode() {
    if (!isChatGPT) return;

    // Target common ChatGPT container classes
    const selectors = [
      '.mx-auto',
      '.max-w-3xl',
      '.max-w-2xl',
      '.max-w-4xl',
      'main',
      '[role="main"]',
      '.conversation',
      'form'
    ];

    selectors.forEach(selector => {
      const elements = document.querySelectorAll(selector);
      elements.forEach(el => {
        if (!el.classList.contains('sidebar') &&
          !el.classList.contains('menu') &&
          !el.parentElement?.classList.contains('sidebar')) {
          el.style.maxWidth = '95%';
          el.style.width = '95%';
        }
      });
    });

    // Handle input areas specifically
    const inputElements = document.querySelectorAll('textarea, [contenteditable="true"]');
    inputElements.forEach(el => {
      el.style.width = '100%';
      el.style.maxWidth = '100%';
    });
  }

  // Apply the changes when the DOM is fully loaded
  if (document.readyState === 'loading') {
    document.addEventListener('DOMContentLoaded', function () {
      applyWideModeToInlineStyles();
      optimizeGeminiInputLayout();
      relocateConversationTitle();
      applyChatGPTWideMode();
    });
  } else {
    applyWideModeToInlineStyles();
    optimizeGeminiInputLayout();
    relocateConversationTitle();
    applyChatGPTWideMode();
  }

  // Create a MutationObserver to watch for dynamically added elements
  if (isGemini) {
    const observer = new MutationObserver(function (mutations) {
      applyWideModeToInlineStyles();
      optimizeGeminiInputLayout();
      relocateConversationTitle();
    });

    // Start observing the document body for changes
    observer.observe(document.body, {
      childList: true,
      subtree: true,
      attributes: true,
      attributeFilter: ['style', 'class']
    });
  }

  // Enhanced ChatGPT observer
  if (isChatGPT) {
    const chatGPTObserver = new MutationObserver(function (mutations) {
      if (chatGPTObserver.timeoutId) {
        return;
      }

      chatGPTObserver.timeoutId = setTimeout(function () {
        applyChatGPTWideMode();
        delete chatGPTObserver.timeoutId;
      }, 300);
    });

    // Observe the entire document for ChatGPT changes
    chatGPTObserver.observe(document.body, {
      childList: true,
      subtree: true,
      attributes: true,
      attributeFilter: ['class', 'style']
    });
  }

  // Claude observer (keeping original logic)
  if (isClaude) {
    const claudeObserver = new MutationObserver(function (mutations) {
      if (claudeObserver.timeoutId) {
        return;
      }

      claudeObserver.timeoutId = setTimeout(function () {
        const inputElements = document.querySelectorAll('textarea, [role="textbox"], div[contenteditable="true"]');
        inputElements.forEach(el => {
          if (el && !el.dataset.widthFixed) {
            el.style.width = '100%';
            el.style.maxWidth = '100%';
            el.dataset.widthFixed = 'true';
          }
        });
        delete claudeObserver.timeoutId;
      }, 500);
    });

    const formElement = document.querySelector('form') || document.body;
    claudeObserver.observe(formElement, {
      childList: true,
      subtree: true,
      attributes: false
    });
  }

})();