GitLab MR Viewed File Counter

Displays viewed/total file count on GitLab merge request pages

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

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

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

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

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

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

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

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

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

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

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

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

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

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

このスクリプトの質問や評価の投稿はこちら通報はこちらへお寄せください
// ==UserScript==
// @name         GitLab MR Viewed File Counter
// @namespace    http://tampermonkey.net/
// @version      1.0.1
// @description  Displays viewed/total file count on GitLab merge request pages
// @match        https://gitlab.com/*/-/merge_requests/*
// @grant        none
// @run-at       document-idle
// @author       bzly
// @license      GPL-3.0-or-later
// ==/UserScript==

(function () {
  'use strict';

  const badge = document.querySelector('.js-changes-tab-count');
  let total = badge.textContent.trim();
  const mrPath = window.location.pathname.match(/.*\/merge_requests\/\d+/)?.[0];
  const mrKey = `code-review-${mrPath}`;
  const pill = document.querySelector('.diffs-tab .gl-badge');

  function getViewedCount() {
    try {
      const viewed = JSON.parse(localStorage.getItem(mrKey));
      return Array.isArray(viewed) ? viewed.length : 0;
    } catch {
      return 0;
    }
  }

  function updateBadge() {
    const viewed = getViewedCount();
    const pct = total > 0 ? Math.round((viewed / total) * 100) : 0;
    badge.textContent = `${viewed}/${total}`;
    if (viewed > 0) {
        // when any files are marked as viewed, gradually turn from the GitLab 'closed' colour to the 'merged' colour.
        pill.style.backgroundColor = `color-mix(in srgb, var(--gl-badge-success-background-color-default) ${pct}%, var(--gl-badge-danger-background-color-default))`;
    } else {
       pill.style.backgroundColor = 'var(--gl-badge-neutral-background-color-default)';
    }
  }

  // update count when GitLab changes the element back at random times
  new MutationObserver(() => {
    if (!badge.textContent.includes('/')) {
      total = badge.textContent.trim();
      updateBadge();
    }
  }).observe(badge, { childList: true, characterData: true, subtree: true });

  // update count on same-tab changes
  const _setItem = localStorage.setItem.bind(localStorage);
  localStorage.setItem = function (key, value) {
    _setItem(key, value);
    if (key === mrKey) updateBadge();
  };

  // update count when going back to 0 viewed
  const _removeItem = localStorage.removeItem.bind(localStorage);
  localStorage.removeItem = function (key) {
    _removeItem(key);
    if (key === mrKey) updateBadge();
  };

  // update count on different-tab change (i.e. you are working across multiple tabs)
  window.addEventListener('storage', (e) => {
    if (e.key === mrKey) updateBadge();
  });
  
  updateBadge();
})();