Codeforces 一键隐藏侧边栏脚本

"Codeforces Sidebar Toggle" 是一个用户脚本(UserScript)的名称,它的功能是切换 Codeforces 编程竞赛网站题目页面的侧边栏显示状态。以下是具体解析:

现新增了功能,可以一键复制Markdown形式的链接,主要方便写题解或者小结时使用,其它功能不变。
最近那个网站的号登不上去了,所以最新版本需要手动复制代码到篡改猴中。


代码全部由AI生成

🛠️ 核心功能:

功能 作用
隐藏侧边栏 移除干扰元素,让题目区域更宽敞
精简顶部按钮 只保留必要按钮(如提交按钮)
内容放大15% 提升代码和题目文本的可读性
自动记忆设置 记住你的偏好(下次访问自动应用)
快捷键切换 (Alt+Q) 快速临时切换,不影响自动隐藏设置

效果演示

未开启
屏幕截图 2025-07-08 070011

开启后
屏幕截图 2025-07-08 065954


添加

首先需要有篡改猴和cf-better
添加新脚本
https://openuserjs.org/scripts/buba_buba/Codeforces_Sidebar_Toggle

代码

// ==UserScript==
// @name         ewe→QwQ
// @namespace    http://codeforces.com/
// @version      3.0
// @description  在 Codeforces 题目页隐藏侧边栏并提供复制 Markdown 按钮
// @match        https://codeforces.com/problemset/problem/*/*
// @match        https://codeforces.com/contest/*/problem/*
// @match        https://codeforces.com/gym/*/problem/*
// @match        https://codeforces.com/group/*/contest/*/problem/*
// @grant        GM_setClipboard
// @run-at       document-idle
// @license      MIT
// ==/UserScript==

(() => {
  'use strict';

  /*** -------------------- 常量与工具 -------------------- ***/
  const STORAGE_KEY = 'cf_auto_hide_sidebar';
  const ICON_URL = 'https://raw.githubusercontent.com/hejiehejiehejiehejie/icon/main/lianjie.png';
  const BTN_SIZE = 18;

  // 预加载图标,减少首次渲染延迟
  new Image().src = ICON_URL;

  const $ = (sel, root = document) => root.querySelector(sel);

  // 判断是否在可编辑输入中(避免快捷键/点击误触)
  const inEditable = (e) => {
    const t = e.target;
    return t && (
      t.isContentEditable ||
      ['INPUT', 'TEXTAREA', 'SELECT'].includes(t.nodeName)
    );
  };

  // 解析 CF 题目 URL,以生成稳定的 Markdown
  function parseCFUrl(u) {
    // 支持 contest/problemset/group/gym 四类
    // 统一提取 (id, index)
    const m =
      u.match(/^https?:\/\/codeforces\.com\/contest\/(\d+)\/problem\/([A-Z]\d*|[A-Z])\/?$/) ||
      u.match(/^https?:\/\/codeforces\.com\/problemset\/problem\/(\d+)\/([A-Z]\d*|[A-Z])\/?$/) ||
      u.match(/^https?:\/\/codeforces\.com\/group\/[^/]+\/contest\/(\d+)\/problem\/([A-Z]\d*|[A-Z])\/?$/) ||
      u.match(/^https?:\/\/codeforces\.com\/gym\/(\d+)\/problem\/([A-Z]\d*|[A-Z])\/?$/);

    return m ? { id: m[1], index: m[2] } : null;
  }

  /*** -------------------- 样式(用类控制) -------------------- ***/
  const STYLE = `
    :root {
      --qwq-zoom: 1.15;
      --qwq-btn-size: ${BTN_SIZE}px;
      --qwq-gap: 10px;
    }

    /* 开关类:整体布局 */
    body.cf-hide-sidebar {
      overflow-x: hidden !important;
    }
    body.cf-hide-sidebar #pageContent {
      width: 100% !important;
      margin-left: 0 !important;
    }
    body.cf-hide-sidebar.zoomed {
      transform-origin: top left;
      transform: scale(var(--qwq-zoom));
      width: calc(100vw / var(--qwq-zoom));
    }

    /* 隐藏侧栏 */
    body.cf-hide-sidebar #sidebar { display: none !important; }

    /* 隐藏右上角多余按钮:仅保留第一个 */
    body.cf-hide-sidebar .topRightDiv > *:not(:first-child) {
      display: none !important;
    }

    /* 兜底按钮样式(若无法复用 CF 样式时) */
    .cf-md-btn {
      display: inline-flex;
      align-items: center;
      justify-content: center;
      vertical-align: middle;
      height: 26px;
      line-height: 26px;
      padding: 0 8px;
      margin-left: 6px;
      border: 1px solid rgba(0,0,0,0.1);
      border-radius: 4px;
      background: #f7f7f7;
      box-sizing: border-box;
      cursor: pointer;
      user-select: none;
    }

    /* 我们的两个小控件与工具栏的对齐 */
    #cfAutoHideSwitch, #cfMarkdownCopyBtn {
      display: inline-flex;
      align-items: center;
      margin-left: var(--qwq-gap);
      vertical-align: middle;
    }

    #cfMarkdownCopyBtn img {
      width: var(--qwq-btn-size);
      height: var(--qwq-btn-size);
      display: block;
    }

    #cfAutoHideSwitch input[type="checkbox"] {
      width: var(--qwq-btn-size);
      height: var(--qwq-btn-size);
      cursor: pointer;
      margin: 0;
      padding: 0;
    }
  `;

  function ensureStyle() {
    if (!$('#qwq-style')) {
      const s = document.createElement('style');
      s.id = 'qwq-style';
      s.textContent = STYLE;
      document.head.appendChild(s);
    }
  }

  /*** -------------------- UI 元素 -------------------- ***/
  function createAutoHideSwitch() {
    const wrap = document.createElement('span');
    wrap.id = 'cfAutoHideSwitch';

    const cb = document.createElement('input');
    cb.type = 'checkbox';
    cb.title = '自动隐藏侧边栏(Alt+Q 快速切换)';
    cb.checked = localStorage.getItem(STORAGE_KEY) === 'on';
    cb.addEventListener('change', () => {
      localStorage.setItem(STORAGE_KEY, cb.checked ? 'on' : 'off');
    });

    wrap.appendChild(cb);
    return wrap;
  }

  function createMarkdownCopyButton() {
    const parsed = parseCFUrl(location.href);
    if (!parsed) return null;

    const md = `[CF${parsed.id}${parsed.index}](${location.href})`;

    const wrap = document.createElement('span');
    wrap.id = 'cfMarkdownCopyBtn';
    wrap.title = '复制 Markdown(Ctrl+M)';

    const img = document.createElement('img');
    img.src = ICON_URL;
    img.alt = '复制MD';

    const copy = () => {
      try {
        if (typeof GM_setClipboard !== 'undefined') {
          GM_setClipboard(md);
        } else if (navigator.clipboard?.writeText) {
          navigator.clipboard.writeText(md);
        } else {
          // 最后兜底
          const ta = document.createElement('textarea');
          ta.value = md;
          ta.style.position = 'fixed';
          ta.style.opacity = '0';
          document.body.appendChild(ta);
          ta.select();
          document.execCommand('copy');
          ta.remove();
        }
        wrap.title = '✅ 已复制!';
        setTimeout(() => (wrap.title = '复制 Markdown(Ctrl+M)'), 1500);
      } catch { /* 忽略 */ }
    };

    wrap.addEventListener('click', (e) => {
      if (inEditable(e)) return;
      copy();
    });

    // 快捷键(输入框内不触发)
    document.addEventListener('keydown', (e) => {
      if (inEditable(e)) return;
      if (e.ctrlKey && e.key.toLowerCase() === 'm') {
        e.preventDefault();
        copy();
      }
    }, { passive: false });

    wrap.appendChild(img);
    return wrap;
  }

  /*** -------------------- 行为控制 -------------------- ***/
  const Sidebar = {
    enable() {
      ensureStyle();
      document.body.classList.add('cf-hide-sidebar', 'zoomed');
    },
    disable() {
      document.body.classList.remove('cf-hide-sidebar', 'zoomed');
    },
    toggle() {
      document.body.classList.toggle('cf-hide-sidebar');
      document.body.classList.toggle('zoomed');
    },
    get active() {
      return document.body.classList.contains('cf-hide-sidebar');
    }
  };

  function setupHotkeys() {
    // Alt+Q 开关(输入框内不触发)
    document.addEventListener('keydown', (e) => {
      if (inEditable(e)) return;
      if (e.altKey && e.key.toLowerCase() === 'q') {
        e.preventDefault();
        Sidebar.toggle();
      }
    }, { passive: false });
  }

  function installIntoToolbar() {
    const toolbar = $('#problemToolbar');
    if (!toolbar || $('#cfAutoHideSwitch')) return false;

    // 安装控件
    const sw = createAutoHideSwitch();
    toolbar.appendChild(sw);

    const mdBtn = createMarkdownCopyButton();
    if (mdBtn) toolbar.appendChild(mdBtn);

    // 根据持久化状态启用布局
    if (localStorage.getItem(STORAGE_KEY) === 'on') {
      Sidebar.enable();
    }

    return true;
  }

  /*** -------------------- 启动流程 -------------------- ***/
  function init() {
    ensureStyle();
    setupHotkeys();

    // 如果工具栏已存在,直接安装;否则观察 DOM 直至出现
    if (!installIntoToolbar()) {
      const mo = new MutationObserver(() => {
        if (installIntoToolbar()) mo.disconnect();
      });
      mo.observe(document.body, { childList: true, subtree: true });
    }
  }

  init();
})();

posted @ 2025-06-04 17:22  he_jie  阅读(31)  评论(0)    收藏  举报