AO3 Adjust Paragraph Margins

Increase or decrease the margins between the paragraphs in a work. Set on a per work or per author basis.

// ==UserScript==
// @name         AO3 Adjust Paragraph Margins
// @description  Increase or decrease the margins between the paragraphs in a work. Set on a per work or per author basis.
// @author       Ifky_
// @namespace    https://greasyforks.org/en/scripts/542569
// @version      1.0.1
// @history      1.0.1 — Reset current setting when resetting everything
// @history      1.0.0 — Increase or decrease the margin between a work's paragraphs
// @match        https://archiveofourown.org/works/*
// @icon         https://archiveofourown.org/images/logo.png
// @license      GPL-3.0-only
// @grant        none
// ==/UserScript==
"use strict";
(function () {
    // Add styles to increase or decrease margin
    // If you want to use different values, you can either edit this code or override it with a site skin
    const styleTag = document.createElement("style");
    styleTag.textContent = `
    #workskin.a-p-m--i [role=article] .userstuff p,
    #workskin.a-p-m--i [role=article].userstuff p {
      margin-block: 3.5em;
    }

    #workskin.a-p-m--d [role=article] .userstuff p,
    #workskin.a-p-m--d [role=article].userstuff p {
      margin-block: .15em;
    }

    #a-p-m fieldset {
      display: block;
    }

    #a-p-m fieldset ul {
      display: flex;
      gap: 1em;
      flex-wrap: wrap;
    }

    #a-p-m fieldset label {
      padding-inline: .75em !important;
    }
  `;
    document.head.appendChild(styleTag);
    // Get necessary elements and data
    const workskin = document.querySelector("#workskin");
    const actions = document.querySelector("#main .work.actions");
    const workId = window.location.pathname.split("/")[2];
    const authorEl = document.querySelector("#workskin a[rel='author'][href*='/users/']");
    const author = authorEl?.textContent ?? null;
    // Get existing settings from local storage
    const getLocal = (key) => {
        const value = localStorage.getItem(key);
        return new Set(value ? value.split(',') : []);
    };
    const addLocal = (key, value) => {
        const local = getLocal(key);
        local.add(value);
        const string = Array.from(local).join(',');
        localStorage.setItem(key, string);
    };
    const removeLocal = (key, value) => {
        const local = getLocal(key);
        local.delete(value);
        const string = Array.from(local).join(',');
        localStorage.setItem(key, string);
    };
    // Get current settings (if they exist)
    const workIncrease = getLocal("a-p-m--w-i");
    const workDecrease = getLocal("a-p-m--w-d");
    const workStandard = getLocal("a-p-m--w-s");
    const authorIncrease = getLocal("a-p-m--a-i");
    const authorDecrease = getLocal("a-p-m--a-d");
    let marginType;
    (function (marginType) {
        marginType[marginType["STANDARD"] = 0] = "STANDARD";
        marginType[marginType["INCREASE"] = 1] = "INCREASE";
        marginType[marginType["DECREASE"] = 2] = "DECREASE";
        marginType[marginType["AUTHOR"] = 3] = "AUTHOR";
    })(marginType || (marginType = {}));
    const setMarginClass = (value) => {
        switch (value) {
            case marginType.STANDARD:
                workskin.classList.remove("a-p-m--i");
                workskin.classList.remove("a-p-m--d");
                break;
            case marginType.INCREASE:
                workskin.classList.add("a-p-m--i");
                workskin.classList.remove("a-p-m--d");
                break;
            case marginType.DECREASE:
                workskin.classList.remove("a-p-m--i");
                workskin.classList.add("a-p-m--d");
                break;
        }
    };
    const setMarginLocal = (key, value) => {
        if (key === "work") {
            switch (value) {
                case marginType.STANDARD:
                    removeLocal("a-p-m--w-i", workId);
                    removeLocal("a-p-m--w-d", workId);
                    addLocal("a-p-m--w-s", workId);
                    break;
                case marginType.INCREASE:
                    addLocal("a-p-m--w-i", workId);
                    removeLocal("a-p-m--w-d", workId);
                    removeLocal("a-p-m--w-s", workId);
                    break;
                case marginType.DECREASE:
                    removeLocal("a-p-m--w-i", workId);
                    addLocal("a-p-m--w-d", workId);
                    removeLocal("a-p-m--w-s", workId);
                    break;
                case marginType.AUTHOR:
                    removeLocal("a-p-m--w-i", workId);
                    removeLocal("a-p-m--w-d", workId);
                    removeLocal("a-p-m--w-s", workId);
                    break;
            }
        }
        else if (key === "author" && author !== null) {
            switch (value) {
                case marginType.STANDARD:
                    removeLocal("a-p-m--a-i", author);
                    removeLocal("a-p-m--a-d", author);
                    break;
                case marginType.INCREASE:
                    addLocal("a-p-m--a-i", author);
                    removeLocal("a-p-m--a-d", author);
                    break;
                case marginType.DECREASE:
                    removeLocal("a-p-m--a-i", author);
                    addLocal("a-p-m--a-d", author);
                    break;
            }
        }
    };
    // Set margin based on setting
    const wi = workIncrease.has(workId);
    const wd = workDecrease.has(workId);
    const ws = workStandard.has(workId);
    const wa = !(wi || wd || ws);
    const ai = authorIncrease.has(author);
    const ad = authorDecrease.has(author);
    const aSt = !(ai || ad);
    let workSetting = 0;
    let authorSetting = 0;
    if (wi) {
        workSetting = marginType.INCREASE;
    }
    else if (wd) {
        workSetting = marginType.DECREASE;
    }
    else if (wa) {
        workSetting = marginType.AUTHOR;
    }
    if (ai) {
        authorSetting = marginType.INCREASE;
    }
    else if (ad) {
        authorSetting = marginType.DECREASE;
    }
    if (workSetting !== marginType.AUTHOR) {
        setMarginClass(workSetting);
    }
    else {
        setMarginClass(authorSetting);
    }
    if (actions) {
        // The HTML for the action button and expandable secondary
        const marginHtml = `
    <li id="a-p-m" aria-haspopup="true">
      <a id="a-p-m--toggle" href="#" class="collapsed">Margins</a>
      <ul id="a-p-m--secondary" class="expandable secondary hidden">
        <form id="a-p-m--form" class="verbose">
          <!-- Work settings -->
          <fieldset>
            <legend>Work settings</legend>
              <ul>
                <li>
                  <input type="radio" value="0" ${ws ? 'checked="checked"' : ''} name="a-p-m--w" id="a-p-m--w-s">
                  <label for="a-p-m--w-s">Standard</label>
                </li>
                <li>
                  <input type="radio" value="1" ${wi ? 'checked="checked"' : ''} name="a-p-m--w" id="a-p-m--w-i">
                  <label for="a-p-m--w-i">Increase</label>
                </li>
                <li>
                  <input type="radio" value="2" ${wd ? 'checked="checked"' : ''} name="a-p-m--w" id="a-p-m--w-d">
                  <label for="a-p-m--w-d">Decrease</label>
                </li>
                <li>
                  <input type="radio" value="3" ${wa ? 'checked="checked"' : ''} name="a-p-m--w" id="a-p-m--w-a">
                  <label for="a-p-m--w-a">Author</label>
                </li>
              </ul>
            </fieldset>

            <!-- Author settings -->
            ${author ?
            `<fieldset>
                <legend>Author settings</legend>
                <ul>
                  <li>
                    <input type="radio" value="0" ${aSt ? 'checked="checked"' : ''} name="a-p-m--a" id="a-p-m--a-s">
                    <label for="a-p-m--a-s">Standard</label>
                  </li>
                  <li>
                    <input type="radio" value="1" ${ai ? 'checked="checked"' : ''} name="a-p-m--a" id="a-p-m--a-i">
                    <label for="a-p-m--a-i">Increase</label>
                  </li>
                  <li>
                    <input type="radio" value="2" ${ad ? 'checked="checked"' : ''} name="a-p-m--a" id="a-p-m--a-d">
                    <label for="a-p-m--a-d">Decrease</label>
                  </li>
                </ul>
              </fieldset>
            ` : ''}

            <!-- Actions -->
            <ul class="actions">
              <li><button type="submit">Apply</button></li>
              <li><button type="button" id="a-p-m--reset">Reset All</button></li>
              <li><button type="button" id="a-p-m--close">Close</button></li>
            </ul>
        </form>
      </ul>
    </li>
    `;
        // Append element to DOM (actions)
        actions.insertAdjacentHTML('afterbegin', marginHtml);
        // Apply new settings: update settings and store new data locally
        const submitApmForm = (e) => {
            e.preventDefault();
            const formData = new FormData(e.target);
            const workSetting = Number(formData.get("a-p-m--w"));
            const authorSetting = Number(formData.get("a-p-m--a"));
            if (workSetting !== marginType.AUTHOR) {
                setMarginClass(workSetting);
            }
            else {
                setMarginClass(authorSetting);
            }
            setMarginLocal("work", workSetting);
            setMarginLocal("author", authorSetting);
        };
        const secondary = document.querySelector("#a-p-m--secondary");
        const form = document.querySelector("#a-p-m--form");
        const reset = document.querySelector("#a-p-m--reset");
        reset.onclick = () => {
            const response = confirm('Resetting all will permanently delete all work settings and author settings. Are you sure?');
            if (response === true) {
                localStorage.removeItem("a-p-m--w-i");
                localStorage.removeItem("a-p-m--w-d");
                localStorage.removeItem("a-p-m--w-s");
                localStorage.removeItem("a-p-m--a-i");
                localStorage.removeItem("a-p-m--a-d");
                setMarginClass(marginType.STANDARD);
                form.reset();
            }
        };
        const toggleButton = document.querySelector("#a-p-m--toggle");
        toggleButton.onclick = () => {
            toggleButton.classList.toggle('collapsed');
            toggleButton.classList.toggle('expanded');
            secondary.classList.toggle('hidden');
        };
        const close = () => {
            toggleButton.classList.add('collapsed');
            toggleButton.classList.remove('expanded');
            secondary.classList.add('hidden');
        };
        const closeButton = document.querySelector("#a-p-m--close");
        closeButton.onclick = close;
        form.addEventListener('submit', (e) => {
            submitApmForm(e);
            close();
        });
    }
})();
长期地址
遇到问题?请前往 GitHub 提 Issues。