Greasy Fork

Greasy Fork is available in English.

Sort directories before files

Sort directories before files in repository tree view

当前为 2025-04-09 提交的版本,查看 最新版本

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴Greasemonkey 油猴子Violentmonkey 暴力猴,才能安装此脚本。

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

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

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

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

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

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

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

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

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

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

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

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

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

// ==UserScript==
// @name        Sort directories before files
// @description Sort directories before files in repository tree view
// @author      anhkhoakz
// @version     1.0.0
// @match       *://git.sr.ht/*/tree(?:/*)?
// @namespace   anhkhoakz
// @icon        https://git.sr.ht/static/logo.png
// @license     GPL-3.0; https://www.gnu.org/licenses/gpl-3.0.html
// ==/UserScript==

(() => {
	"use strict";

	/**
	 * The container element for the tree list.
	 * @type {HTMLElement|null}
	 */
	const tree_list = document.querySelector(".tree-list");
	if (!tree_list) {
		// Exit if the tree list is not found.
		return;
	}

	/**
	 * Indices of directory rows.
	 * @type {number[]}
	 */
	const trees = [];
	/**
	 * Indices of file rows.
	 * @type {number[]}
	 */
	const blobs = [];
	/**
	 * List of name elements in the tree list.
	 * @type {NodeListOf<HTMLElement>}
	 */
	const names = tree_list.querySelectorAll(".name");

	for (let index = 0; index < names.length; index++) {
		const name = names[index];
		if (name.classList.contains("tree")) {
			trees.push(index);
		} else if (name.classList.contains("blob")) {
			blobs.push(index);
		}
	}

	/**
	 * Total number of cells in the tree list.
	 * @type {number}
	 */
	const num_cells = tree_list.children.length;
	/**
	 * Total number of rows in the tree list.
	 * @type {number}
	 */
	const num_rows = names.length;
	/**
	 * Number of columns in the tree list.
	 * @type {number}
	 */
	const num_columns = num_cells / num_rows;

	if (num_columns * num_rows !== num_cells) {
		return;
	}

	/**
	 * Sorted list of tree list children.
	 * @type {Element[]}
	 */
	const tree_list_sorted = Array.from(tree_list.children).sort((a, b) => {
		/**
		 * Index of element `a` in the tree list.
		 * @type {number}
		 */
		const index_a = Array.prototype.indexOf.call(tree_list.children, a);
		/**
		 * Index of element `b` in the tree list.
		 * @type {number}
		 */
		const index_b = Array.prototype.indexOf.call(tree_list.children, b);

		/**
		 * Row index of element `a`.
		 * @type {number}
		 */
		const row_a = Math.trunc(index_a / num_columns);
		/**
		 * Row index of element `b`.
		 * @type {number}
		 */
		const row_b = Math.trunc(index_b / num_columns);

		/**
		 * Whether row `a` is a directory.
		 * @type {boolean}
		 */
		const row_a_is_dir = trees.indexOf(row_a) !== -1;
		/**
		 * Whether row `b` is a directory.
		 * @type {boolean}
		 */
		const row_b_is_dir = trees.indexOf(row_b) !== -1;

		if (row_a_is_dir !== row_b_is_dir) {
			return row_a_is_dir ? -1 : 1;
		}

		return index_a - index_b;
	});

	for (const child of tree_list_sorted) {
		tree_list.appendChild(child);
	}
})();