GitHub README before files

Moves the rendered repository README above the file list on GitHub repo home pages

2026/08/03のページです。最新版はこちら

スクリプトをインストールするには、Tampermonkey, GreasemonkeyViolentmonkey のような拡張機能のインストールが必要です。

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

スクリプトをインストールするには、TampermonkeyViolentmonkey のような拡張機能のインストールが必要です。

スクリプトをインストールするには、TampermonkeyUserscripts のような拡張機能のインストールが必要です。

このスクリプトをインストールするには、Tampermonkeyなどの拡張機能をインストールする必要があります。

このスクリプトをインストールするには、ユーザースクリプト管理ツールの拡張機能をインストールする必要があります。

(ユーザースクリプト管理ツールは設定済みなのでインストール!)

このスタイルをインストールするには、Stylusなどの拡張機能をインストールする必要があります。

このスタイルをインストールするには、Stylus などの拡張機能をインストールする必要があります。

このスタイルをインストールするには、Stylus tなどの拡張機能をインストールする必要があります。

このスタイルをインストールするには、ユーザースタイル管理用の拡張機能をインストールする必要があります。

このスタイルをインストールするには、ユーザースタイル管理用の拡張機能をインストールする必要があります。

このスタイルをインストールするには、ユーザースタイル管理用の拡張機能をインストールする必要があります。

(ユーザースタイル管理ツールは設定済みなのでインストール!)

このスクリプトの質問や評価の投稿はこちら通報はこちらへお寄せください
// ==UserScript==
// @name         GitHub README before files
// @namespace    https://github.com/
// @version      4.0
// @description  Moves the rendered repository README above the file list on GitHub repo home pages
// @license      MIT
// @match        https://github.com/*/*
// @match        https://github.com/*/*/
// @exclude      https://github.com/*/*/blob/*
// @exclude      https://github.com/*/*/tree/*
// @exclude      https://github.com/*/*/issues*
// @exclude      https://github.com/*/*/pull*
// @exclude      https://github.com/*/*/actions*
// @exclude      https://github.com/*/*/projects*
// @exclude      https://github.com/*/*/wiki*
// @exclude      https://github.com/*/*/settings*
// @exclude      https://github.com/*/*/security*
// @exclude      https://github.com/*/*/network*
// @exclude      https://github.com/*/*/stargazers*
// @exclude      https://github.com/*/*/watchers*
// @exclude      https://github.com/*/*/forks*
// @exclude      https://github.com/*/*/releases*
// @exclude      https://github.com/*/*/tags*
// @exclude      https://github.com/*/*/commits*
// @exclude      https://github.com/*/*/branches*
// @exclude      https://github.com/*/*/compare/*
// @run-at       document-idle
// @grant        none
// ==/UserScript==

(function () {
  "use strict";

  let rafId = 0;
  let lastUrl = location.href;

  function isRepoHome() {
    if (location.hostname !== "github.com") return false;
    const parts = location.pathname
      .replace(/^\/+|\/+$/g, "")
      .split("/")
      .filter(Boolean);
    return parts.length === 2;
  }

  function getReadmeSection() {
    const readme = document.querySelector("#readme");
    if (!readme) return null;
    return readme.closest("section") || readme.parentElement;
  }

  function getFilesSection() {
    const heading = [...document.querySelectorAll("h2, h3")].find((el) =>
      /folders?\s+and\s+files/i.test(el.textContent || ""),
    );

    if (heading) {
      return heading.closest("section, div");
    }

    return (
      document
        .querySelector('[aria-labelledby="folders-and-files"]')
        ?.closest("section, div") ||
      document
        .querySelector('table[aria-labelledby="folders-and-files"]')
        ?.closest("section, div") ||
      document.querySelector('[role="tree"]')?.closest("section, div") ||
      null
    );
  }

  function moveReadme() {
    if (!isRepoHome()) return;

    const readmeSection = getReadmeSection();
    const filesSection = getFilesSection();

    if (!readmeSection || !filesSection) return;
    if (!readmeSection.parentElement || !filesSection.parentElement) return;
    if (readmeSection === filesSection) return;
    if (readmeSection.parentElement !== filesSection.parentElement) return;

    const relation = filesSection.compareDocumentPosition(readmeSection);
    const readmeAlreadyBefore = Boolean(
      relation & Node.DOCUMENT_POSITION_PRECEDING,
    );
    if (readmeAlreadyBefore) return;

    filesSection.parentElement.insertBefore(readmeSection, filesSection);
  }

  function scheduleMove() {
    if (rafId) cancelAnimationFrame(rafId);
    rafId = requestAnimationFrame(() => {
      rafId = 0;
      moveReadme();
    });
  }

  const observer = new MutationObserver(() => {
    if (location.href !== lastUrl) {
      lastUrl = location.href;
    }
    scheduleMove();
  });

  observer.observe(document.documentElement, {
    childList: true,
    subtree: true,
  });

  window.addEventListener("load", scheduleMove, { once: true });
  document.addEventListener("readystatechange", scheduleMove);
  scheduleMove();
})();