Gemini Singularity (V5.7 Lite++)

Gemini 聊天页面轻量美化:更舒服的排版 + 发光代码块 & 表格(保持原生布局),主题自适应 + 可访问性增强。

您需要先安装一个扩展,例如 篡改猴Greasemonkey暴力猴,之后才能安装此脚本。

You will need to install an extension such as Tampermonkey to install this script.

您需要先安装一个扩展,例如 篡改猴暴力猴,之后才能安装此脚本。

您需要先安装一个扩展,例如 篡改猴Userscripts ,之后才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。

您需要先安装用户脚本管理器扩展后才能安装此脚本。

(我已经安装了用户脚本管理器,让我安装!)

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

(我已经安装了用户样式管理器,让我安装!)

// ==UserScript==
// @name         Gemini Singularity (V5.7 Lite++)
// @namespace    http://tampermonkey.net/
// @version      5.7
// @description  Gemini 聊天页面轻量美化:更舒服的排版 + 发光代码块 & 表格(保持原生布局),主题自适应 + 可访问性增强。
// @author       GQLJ + GPT
// @match        https://gemini.google.com/*
// @icon         data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCA2NCA2NCI+CiAgPGRlZnM+CiAgICA8bGluZWFyR3JhZGllbnQgaWQ9ImJnIiB4MT0iMCIgeTE9IjAiIHgyPSIxIiB5Mj0iMSI+CiAgICAgIDxzdG9wIG9mZnNldD0iMCUiIHN0b3AtY29sb3I9IiMxMTE4MjciLz4KICAgICAgPHN0b3Agb2Zmc2V0PSIxMDAlIiBzdG9wLWNvbG9yPSIjMDIwNjE3Ii8+CiAgICA8L2xpbmVhckdyYWRpZW50PgogICAgPHJhZGlhbEdyYWRpZW50IGlkPSJnbG93IiBjeD0iNTAlIiBjeT0iMCUiIHI9IjcwJSI+CiAgICAgIDxzdG9wIG9mZnNldD0iMCUiIHN0b3AtY29sb3I9IiM2MGE1ZmEiIHN0b3Atb3BhY2l0eT0iMC45Ii8+CiAgICAgIDxzdG9wIG9mZnNldD0iMTAwJSIgc3RvcC1jb2xvcj0iIzFkNGVkOCIgc3RvcC1vcGFjaXR5PSIwLjAiLz4KICAgIDwvcmFkaWFsR3JhZGllbnQ+CiAgPC9kZWZzPgogIDxyZWN0IHg9IjQiIHk9IjQiIHdpZHRoPSI1NiIgaGVpZ2h0PSI1NiIgcng9IjE0IiBmaWxsPSJ1cmwoI2JnKSIvPgogIDxjaXJjbGUgY3g9IjMyIiBjeT0iMTgiIHI9IjE4IiBmaWxsPSJ1cmwoI2dsb3cpIiAvPgogIDx0ZXh0IHg9IjUwJSIgeT0iNTYlIiB0ZXh0LWFuY2hvcj0ibWlkZGxlIiBmaWxsPSIjZTVlN2ViIiBmb250LWZhbWlseT0ic3lzdGVtLXVpLCAtYXBwbGUtc3lzdGVtLCBCbGlua01hY1N5c3RlbUZvbnQsICdTZWdvZSBVSScsIHNhbnMtc2VyaWYiIGZvbnQtc2l6ZT0iMzAiIGZvbnQtd2VpZ2h0PSI3MDAiPgogICAgRwogIDwvdGV4dD4KPC9zdmc+
// @grant        GM_addStyle
// @grant        GM_getValue
// @grant        GM_setValue
// @grant        GM_registerMenuCommand
// @run-at       document-start
// @license      MIT
// ==/UserScript==

(function () {
  'use strict';

  const DEBUG = false;
  if (window.top !== window.self) return;

  // =========================
  //  可配置项(菜单开关)
  // =========================
  const DEFAULTS = {
    maxWidth: 980,
    fontSize: 17,
    fontSizeMobile: 16,

    enableRemoteFonts: true,
    enableGlow: true,
    enableEntryAnim: true,
    justifyText: true,
    roundImages: true,
    tableMobileScroll: true,
  };

  const cfg = {
    maxWidth: Number(GM_getValue('maxWidth', DEFAULTS.maxWidth)),
    fontSize: Number(GM_getValue('fontSize', DEFAULTS.fontSize)),
    fontSizeMobile: Number(GM_getValue('fontSizeMobile', DEFAULTS.fontSizeMobile)),

    enableRemoteFonts: !!GM_getValue('enableRemoteFonts', DEFAULTS.enableRemoteFonts),
    enableGlow: !!GM_getValue('enableGlow', DEFAULTS.enableGlow),
    enableEntryAnim: !!GM_getValue('enableEntryAnim', DEFAULTS.enableEntryAnim),
    justifyText: !!GM_getValue('justifyText', DEFAULTS.justifyText),
    roundImages: !!GM_getValue('roundImages', DEFAULTS.roundImages),
    tableMobileScroll: !!GM_getValue('tableMobileScroll', DEFAULTS.tableMobileScroll),
  };

  function toggle(key) {
    GM_setValue(key, !cfg[key]);
    window.location.reload();
  }

  try {
    GM_registerMenuCommand(`远程字体: ${cfg.enableRemoteFonts ? '开' : '关'}`, () => toggle('enableRemoteFonts'));
    GM_registerMenuCommand(`发光效果: ${cfg.enableGlow ? '开' : '关'}`, () => toggle('enableGlow'));
    GM_registerMenuCommand(`入场动画: ${cfg.enableEntryAnim ? '开' : '关'}`, () => toggle('enableEntryAnim'));
    GM_registerMenuCommand(`段落两端对齐: ${cfg.justifyText ? '开' : '关'}`, () => toggle('justifyText'));
    GM_registerMenuCommand(`图片圆角: ${cfg.roundImages ? '开' : '关'}`, () => toggle('roundImages'));
    GM_registerMenuCommand(`移动端表格横滑: ${cfg.tableMobileScroll ? '开' : '关'}`, () => toggle('tableMobileScroll'));
  } catch (_) {}

  // =========================
  //  字体加载(可关)— 用 link 替代 @import
  // =========================
  function injectFonts() {
    if (!cfg.enableRemoteFonts) return;
    const head = document.head || document.documentElement;

    const pre1 = document.createElement('link');
    pre1.rel = 'preconnect';
    pre1.href = 'https://fonts.googleapis.com';
    head.appendChild(pre1);

    const pre2 = document.createElement('link');
    pre2.rel = 'preconnect';
    pre2.href = 'https://fonts.gstatic.com';
    pre2.crossOrigin = 'anonymous';
    head.appendChild(pre2);

    const link = document.createElement('link');
    link.rel = 'stylesheet';
    link.href =
      'https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700;750&family=JetBrains+Mono:wght@400;500;600&display=swap';
    head.appendChild(link);
  }
  injectFonts();

  // =========================
  //  阴影强度(可关)
  // =========================
  const glowShadows = cfg.enableGlow
    ? `
      box-shadow:
        0 0 0 1px rgba(255,255,255,0.04),
        0 0 22px var(--singularity-glow-soft),
        0 22px 55px -18px rgba(0,0,0,0.7) !important;
    `
    : `
      box-shadow:
        0 0 0 1px rgba(255,255,255,0.04),
        0 18px 45px -22px rgba(0,0,0,0.55) !important;
    `;

  const glowShadowsHover = cfg.enableGlow
    ? `
      box-shadow:
        0 0 0 1px var(--singularity-glow-strong),
        0 0 40px var(--singularity-glow-strong),
        0 25px 65px -20px rgba(0,0,0,0.85) !important;
    `
    : `
      box-shadow:
        0 0 0 1px rgba(255,255,255,0.06),
        0 22px 55px -24px rgba(0,0,0,0.75) !important;
    `;

  const tableShadows = cfg.enableGlow
    ? `
      box-shadow:
        0 0 0 1px rgba(255,255,255,0.03),
        0 0 18px var(--singularity-glow-soft),
        0 4px 20px rgba(0,0,0,0.05);
    `
    : `
      box-shadow:
        0 0 0 1px rgba(255,255,255,0.03),
        0 4px 18px rgba(0,0,0,0.05);
    `;

  const tableShadowsHover = cfg.enableGlow
    ? `
      box-shadow:
        0 0 0 1px var(--singularity-glow-strong),
        0 0 28px var(--singularity-glow-strong),
        0 6px 28px rgba(0,0,0,0.12);
    `
    : `
      box-shadow:
        0 0 0 1px rgba(255,255,255,0.05),
        0 6px 26px rgba(0,0,0,0.12);
    `;

  const entryAnimCSS = cfg.enableEntryAnim
    ? `
      @keyframes singularFadeIn {
        0% { opacity: 0; transform: translateY(8px); }
        100% { opacity: 1; transform: translateY(0); }
      }
      .model-response-text > :is(p, h1, h2, h3, h4, blockquote, table, ul, ol, code-block, mjx-container[display="true"]),
      markdown-renderer > :is(p, h1, h2, h3, h4, blockquote, table, ul, ol, code-block, mjx-container[display="true"]) {
        animation: singularFadeIn 0.45s var(--ease-out-expo) both;
      }
    `
    : `
      .model-response-text > *,
      markdown-renderer > * { animation: none !important; }
    `;

  const justifyCSS = cfg.justifyText
    ? `
      :where(.model-response-text, markdown-renderer) p {
        text-align: justify;
        text-justify: inter-ideograph;
      }
    `
    : `
      :where(.model-response-text, markdown-renderer) p { text-align: left; }
    `;

  const imagesCSS = cfg.roundImages
    ? `
      :where(.model-response-text, markdown-renderer) img {
        max-width: 100%;
        height: auto;
        border-radius: 14px;
        box-shadow: 0 8px 28px rgba(0,0,0,0.08);
      }
    `
    : `
      :where(.model-response-text, markdown-renderer) img {
        max-width: 100%;
        height: auto;
      }
    `;

  const tableMobileCSS = cfg.tableMobileScroll
    ? `
      @media (max-width: 768px) {
        :where(.model-response-text, markdown-renderer) table {
          display: block;
          overflow-x: auto;
          -webkit-overflow-scrolling: touch;
        }
      }
    `
    : '';

  // =========================
  //  CSS
  // =========================
  const css = `
    :root {
      --ease-out-expo: cubic-bezier(0.19, 1, 0.22, 1);
      --glass-blur: blur(20px) saturate(180%);

      --body-font: ${cfg.enableRemoteFonts ? `'Inter', ` : ''}system-ui, -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif;
      --code-font: ${cfg.enableRemoteFonts ? `'JetBrains Mono', ` : ''}'Fira Code', 'Menlo', ui-monospace, SFMono-Regular, monospace;

      --accent: #4285f4;
      --accent-strong: #1a73e8;
      --accent-soft: rgba(66, 133, 244, 0.08);

      --singularity-glow-soft: rgba(138, 180, 248, 0.26);
      --singularity-glow-strong: rgba(138, 180, 248, 0.55);

      --singularity-max-width: ${cfg.maxWidth}px;
      --singularity-font-size: ${cfg.fontSize}px;
      --singularity-font-size-mobile: ${cfg.fontSizeMobile}px;

      /* 代码块:主题自适应变量(默认按浅色) */
      --sg-code-bg: var(--gm-surface-variant, #f6f7f8);
      --sg-code-text: var(--gm-on-surface, #202124);
      --sg-code-border: var(--gm-outline-variant, rgba(0,0,0,0.10));
      --sg-code-header-bg: rgba(255,255,255,0.75);
      --sg-code-header-sep: rgba(0,0,0,0.06);

      scrollbar-width: thin;
      scrollbar-color: var(--gm-outline-variant, rgba(128,128,128,0.2)) transparent;
    }

    /* 深色主题兜底 */
    @media (prefers-color-scheme: dark) {
      :root {
        --sg-code-bg: #050608;
        --sg-code-text: #e4e4e4;
        --sg-code-border: var(--gm-outline-variant, rgba(255,255,255,0.12));
        --sg-code-header-bg: rgba(30, 30, 30, 0.6);
        --sg-code-header-sep: rgba(255,255,255,0.06);
      }
    }

    body {
      -webkit-font-smoothing: antialiased;
      -moz-osx-font-smoothing: grayscale;
      text-rendering: optimizeLegibility;
    }

    ::-webkit-scrollbar { width: 6px; height: 6px; }
    ::-webkit-scrollbar-track { background: transparent; }
    ::-webkit-scrollbar-thumb {
      background: var(--gm-outline-variant, rgba(128,128,128,0.2));
      border-radius: 99px;
      border: 2px solid transparent;
      background-clip: content-box;
    }
    ::-webkit-scrollbar-thumb:hover { background-color: var(--gm-outline, rgba(128,128,128,0.5)); }

    .conversation-container,
    .bottom-container,
    .input-area-container {
      max-width: var(--singularity-max-width) !important;
      margin: 0 auto !important;
      padding-inline: 12px;
    }

    ::selection { background: rgba(66, 133, 244, 0.25); color: inherit; }

    ${entryAnimCSS}

    @media (prefers-reduced-motion: reduce) {
      .model-response-text > *,
      markdown-renderer > * { animation: none !important; }
      code-block,
      .model-response-text table,
      markdown-renderer table { transition: none !important; }
    }

    /* 更强的“减少透明/特效”降级(支持就用,不支持也不影响) */
    @media (prefers-reduced-transparency: reduce) {
      code-block, :where(.model-response-text, markdown-renderer) table {
        box-shadow: none !important;
      }
      .code-block-decoration {
        backdrop-filter: none !important;
        -webkit-backdrop-filter: none !important;
      }
    }

    /* ====== 正文排版 ====== */
    :where(.model-response-text, markdown-renderer) {
      font-family: var(--body-font) !important;
      font-size: var(--singularity-font-size) !important;
      line-height: 1.85 !important;
      letter-spacing: 0.012em;
      color: var(--gm-on-surface) !important;
      hyphens: auto;
    }

    :where(.model-response-text, markdown-renderer) :is(h1, h2, h3, h4) {
      font-weight: 750 !important;
      letter-spacing: -0.02em;
      margin-top: 1.8em !important;
      margin-bottom: 0.8em !important;
      color: var(--gm-on-surface);
      position: relative;
    }
    :where(.model-response-text, markdown-renderer) h1 { font-size: 1.6em !important; }
    :where(.model-response-text, markdown-renderer) h2 { font-size: 1.35em !important; }
    :where(.model-response-text, markdown-renderer) h3 { font-size: 1.18em !important; }
    :where(.model-response-text, markdown-renderer) h4 { font-size: 1.05em !important; }

    :where(.model-response-text, markdown-renderer) :is(h1, h2, h3, h4)::after {
      content: '';
      position: absolute;
      left: 0;
      bottom: -6px;
      width: 72px;
      height: 2px;
      border-radius: 99px;
      background: linear-gradient(90deg, var(--accent), transparent);
      opacity: 0.55;
    }

    :where(.model-response-text, markdown-renderer) p {
      margin-bottom: 1.8em !important;
      max-width: 100%;
    }

    ${justifyCSS}

    @media (max-width: 768px) {
      :where(.model-response-text, markdown-renderer) {
        font-size: var(--singularity-font-size-mobile) !important;
      }
      :where(.model-response-text, markdown-renderer) p { text-align: left !important; }
    }

    /* 列表 */
    :where(.model-response-text, markdown-renderer) ul li::marker { color: var(--accent); }
    :where(.model-response-text, markdown-renderer) ol li::marker {
      color: var(--accent);
      font-weight: 600;
      font-variant-numeric: tabular-nums;
    }
    :where(.model-response-text, markdown-renderer) li {
      padding-left: 4px;
      margin-bottom: 0.8em !important;
    }

    /* 链接:hover 不抖动 */
    :where(.model-response-text, markdown-renderer) a {
      text-decoration: none !important;
      color: var(--accent-strong) !important;
      border-bottom: 1.5px solid rgba(26, 115, 232, 0.3);
      transition: box-shadow 0.18s ease-out, background 0.18s ease-out, border-color 0.18s ease-out;
      font-weight: 500;
      border-radius: 4px;
    }
    :where(.model-response-text, markdown-renderer) a:hover {
      border-bottom-color: var(--accent-strong);
      background: var(--accent-soft);
      box-shadow: 0 0 0 4px var(--accent-soft);
    }
    @media (prefers-color-scheme: dark) {
      :where(.model-response-text, markdown-renderer) a {
        color: #8ab4f8 !important;
        border-color: rgba(138, 180, 248, 0.35);
      }
    }

    /* ===== 可访问性:键盘 focus ring(范围尽量收在主区) ===== */
    :where(.conversation-container, .bottom-container, .input-area-container)
      :is(a, button, [role="button"], input, textarea, select):focus-visible {
      outline: 2px solid var(--accent) !important;
      outline-offset: 3px !important;
      border-radius: 8px;
      box-shadow: 0 0 0 4px var(--accent-soft) !important;
    }

    /* ====== 代码块(主题自适应) ====== */
    code-block {
      display: block;
      margin: 32px 0 !important;
      border-radius: 16px !important;
      border: 1px solid var(--sg-code-border) !important;
      background: var(--sg-code-bg) !important;
      ${glowShadows}
      overflow: hidden !important;
      position: relative;
      z-index: 1;
      transition: box-shadow 0.22s var(--ease-out-expo), border-color 0.22s var(--ease-out-expo);
    }
    code-block:hover {
      border-color: var(--singularity-glow-strong);
      ${glowShadowsHover}
    }

    .code-block-decoration {
      height: 50px !important;
      display: flex;
      align-items: center;
      padding: 0 22px !important;
      background: var(--sg-code-header-bg) !important;
      border-bottom: 1px solid var(--sg-code-header-sep);
    }
    @supports (backdrop-filter: blur(1px)) or (-webkit-backdrop-filter: blur(1px)) {
      .code-block-decoration {
        backdrop-filter: var(--glass-blur) !important;
        -webkit-backdrop-filter: var(--glass-blur) !important;
      }
    }

    .code-block-decoration::before {
      content: '';
      width: 12px;
      height: 12px;
      border-radius: 50%;
      background: #ff5f56;
      box-shadow: 20px 0 0 #ffbd2e, 40px 0 0 #27c93f;
      margin-right: 20px;
      opacity: 0.8;
      transition: opacity 0.3s;
    }
    code-block:hover .code-block-decoration::before { opacity: 1; }

    /* 仅影响 code-block 内 pre */
    code-block pre {
      background: transparent !important;
      padding: 24px !important;
      font-family: var(--code-font) !important;
      font-size: 14.5px !important;
      line-height: 1.7 !important;
      color: var(--sg-code-text) !important;
      text-shadow: none;
      overflow-x: auto;
      tab-size: 4;
    }
    @media (prefers-color-scheme: dark) {
      code-block pre { text-shadow: 0 1px 2px rgba(0,0,0,0.5); }
    }

    /* 行内代码 */
    :where(.model-response-text, markdown-renderer) :not(pre) > code {
      font-family: var(--code-font) !important;
      background: var(--gm-secondary-container, rgba(0,0,0,0.05)) !important;
      color: var(--gm-on-secondary-container, #d93025) !important;
      padding: 4px 8px !important;
      border-radius: 8px !important;
      font-size: 0.85em !important;
      border: 1px solid var(--gm-outline-variant, rgba(0,0,0,0.05));
      vertical-align: baseline;
      font-weight: 600;
      word-break: break-word;
    }

    /* 引用块 */
    :where(.model-response-text, markdown-renderer) blockquote {
      border: none !important;
      position: relative;
      margin: 2.4em 0 !important;
      padding: 1.6em 2em !important;
      background: linear-gradient(
        135deg,
        var(--gm-surface-variant, rgba(66, 133, 244, 0.05)) 0%,
        rgba(66, 133, 244, 0.01) 100%
      );
      border-radius: 16px;
      color: var(--gm-on-surface-variant) !important;
    }
    :where(.model-response-text, markdown-renderer) blockquote::before {
      content: '❝';
      position: absolute;
      top: 10px;
      right: 14px;
      font-size: 28px;
      opacity: 0.18;
      color: var(--accent);
      pointer-events: none;
    }
    :where(.model-response-text, markdown-renderer) blockquote::after {
      content: '';
      position: absolute;
      left: 0;
      top: 12px;
      bottom: 12px;
      width: 3px;
      background: var(--accent);
      border-radius: 0 4px 4px 0;
      box-shadow: 2px 0 8px rgba(66, 133, 244, 0.3);
    }

    /* 表格 */
    :where(.model-response-text, markdown-renderer) table {
      border-collapse: separate !important;
      border-spacing: 0;
      width: 100%;
      margin: 2.4em 0 !important;
      border-radius: 12px;
      overflow: hidden;
      border: 1px solid var(--gm-outline-variant, rgba(128,128,128,0.2));
      ${tableShadows}
      transition: box-shadow 0.22s var(--ease-out-expo), border-color 0.22s var(--ease-out-expo);
    }
    :where(.model-response-text, markdown-renderer) table:hover {
      border-color: var(--singularity-glow-strong);
      ${tableShadowsHover}
    }
    :where(.model-response-text, markdown-renderer) th {
      background: var(--gm-surface-variant, #f1f3f4);
      color: var(--gm-on-surface);
      padding: 16px 18px !important;
      font-weight: 700;
      font-size: 0.95em;
      text-transform: uppercase;
      letter-spacing: 0.05em;
      white-space: nowrap;
    }
    :where(.model-response-text, markdown-renderer) td {
      padding: 14px 18px !important;
      border-top: 1px solid var(--gm-outline-variant, rgba(128,128,128,0.05));
      background: var(--gm-surface, transparent);
      transition: background 0.1s;
    }
    :where(.model-response-text, markdown-renderer) tr:hover td {
      background: var(--gm-secondary-container, rgba(66, 133, 244, 0.05));
    }
    ${tableMobileCSS}

    /* LaTeX */
    :where(.model-response-text, markdown-renderer) mjx-container[display="true"] {
      margin: 2.5em 0 !important;
      padding: 1.5em 2em !important;
      background: var(--gm-surface-variant, rgba(128,128,128,0.03));
      border-radius: 12px;
      border: 1px solid var(--gm-outline-variant, transparent);
      color: var(--gm-on-surface) !important;
      overflow-x: auto;
    }

    ${imagesCSS}

    .input-area-container { padding-bottom: 40px !important; }

    /* ===== 高对比模式/强制颜色:禁用发光/渐变,确保可读 ===== */
    @media (forced-colors: active) {
      code-block,
      :where(.model-response-text, markdown-renderer) table,
      :where(.model-response-text, markdown-renderer) blockquote,
      :where(.model-response-text, markdown-renderer) mjx-container[display="true"] {
        box-shadow: none !important;
        background: Canvas !important;
        color: CanvasText !important;
        border: 1px solid CanvasText !important;
      }
      .code-block-decoration {
        background: Canvas !important;
        border-bottom: 1px solid CanvasText !important;
        backdrop-filter: none !important;
        -webkit-backdrop-filter: none !important;
      }
      :where(.model-response-text, markdown-renderer) :is(h1,h2,h3,h4)::after {
        background: CanvasText !important;
        opacity: 1 !important;
      }
    }
  `;

  if (typeof GM_addStyle !== 'undefined') {
    GM_addStyle(css);
  } else {
    const style = document.createElement('style');
    style.textContent = css;
    document.documentElement.appendChild(style);
  }

  if (DEBUG) console.log('Gemini Singularity (V5.7 Lite++) Activated.', cfg);
})();