Notion Toggles: Floating Heads, Comment Highlights, and Margin Comments

Toggle visibility of floating head icons, comment highlighting, and margin comment boxes in Notion

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

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

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

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

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

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

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

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

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

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

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

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

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

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

このスクリプトの質問や評価の投稿はこちら通報はこちらへお寄せください
// ==UserScript==
// @name         Notion Toggles: Floating Heads, Comment Highlights, and Margin Comments
// @namespace    http://tampermonkey.net/
// @version      3.3
// @description  Toggle visibility of floating head icons, comment highlighting, and margin comment boxes in Notion
// @match        https://www.notion.so/*
// @grant        GM_setValue
// @grant        GM_getValue
// @grant        GM_addStyle
// @run-at       document-idle
// @license      MIT
// ==/UserScript==

(function() {
    'use strict';

    let isHeadsVisible = GM_getValue('isHeadsVisible', true);
    let isCommentsHighlighted = GM_getValue('isCommentsHighlighted', true);
    let isMarginCommentsVisible = GM_getValue('isMarginCommentsVisible', true);

    // Add custom styles
    GM_addStyle(`
        #notionToggleButtonContainer {
            position: fixed;
            bottom: calc(100px + env(safe-area-inset-bottom));
            right: 16px;
            display: flex;
            flex-direction: column;
            gap: 8px;
            z-index: 110;
            transition: transform 200ms;
        }
        #notionToggleButtonContainer button {
            width: 36px;
            height: 36px;
            border-radius: 100%;
            background: rgb(239, 239, 238);
            box-shadow: rgba(15, 15, 15, 0.1) 0px 0px 0px 1px, rgba(15, 15, 15, 0.1) 0px 2px 4px;
            display: flex;
            align-items: center;
            justify-content: center;
            cursor: pointer;
            transition: background-color 0.3s ease;
        }
        #notionToggleButtonContainer button:hover {
            background-color: #e0e0e0;
        }
        #notionToggleButtonContainer svg {
            width: 20px;
            height: 20px;
        }
    `);

    function debounce(func, wait) {
        let timeout;
        return function executedFunction(...args) {
            const later = () => {
                clearTimeout(timeout);
                func(...args);
            };
            clearTimeout(timeout);
            timeout = setTimeout(later, wait);
        };
    }

    const updateFloatingHeadsVisibility = debounce(() => {
        const containers = document.querySelectorAll('.notion-presence-container');
        containers.forEach(container => {
            container.style.setProperty('display', isHeadsVisible ? '' : 'none', 'important');
        });
    }, 100);

    const updateCommentHighlighting = debounce(() => {
        const commentedElements = document.querySelectorAll('.discussion-level-1');

        commentedElements.forEach(element => {
            if (isCommentsHighlighted) {
                element.style.setProperty('background-color','rgba(255,203,0,.12');
                element.style.setProperty('border-bottom', '2px solid rgba(255,203,0,.35)');
            } else {
                element.style.setProperty('background-color', 'transparent', 'important');
                element.style.setProperty('border-bottom', 'none', 'important');
            }

            // Also update child elements
            element.querySelectorAll('*').forEach(child => {
                if (isCommentsHighlighted) {
                    element.style.setProperty('background-color','rgba(255,203,0,.12');
                    element.style.setProperty('border-bottom', '2px solid rgba(255,203,0,.35)');
                } else {
                    child.style.setProperty('background-color', 'transparent', 'important');
                    child.style.setProperty('border-bottom', 'none', 'important');
                }
            });
        });
    }, 100);

    const updateMarginCommentsVisibility = debounce(() => {
        const marginComments = document.querySelectorAll('.notion-margin-discussion-item');
        marginComments.forEach(comment => {
            comment.style.setProperty('display', isMarginCommentsVisible ? '' : 'none', 'important');
        });
    }, 100);

    function toggleFloatingHeads() {
        isHeadsVisible = !isHeadsVisible;
        GM_setValue('isHeadsVisible', isHeadsVisible);
        updateFloatingHeadsVisibility();
        updateButtonAppearance('heads');
    }

    function toggleCommentHighlighting() {
        isCommentsHighlighted = !isCommentsHighlighted;
        GM_setValue('isCommentsHighlighted', isCommentsHighlighted);
        updateCommentHighlighting();
        updateButtonAppearance('comments');
    }

    function toggleMarginComments() {
        isMarginCommentsVisible = !isMarginCommentsVisible;
        GM_setValue('isMarginCommentsVisible', isMarginCommentsVisible);
        updateMarginCommentsVisibility();
        updateButtonAppearance('margin');
    }

    function updateButtonAppearance(buttonType) {
        const button = document.getElementById(`toggle${buttonType === 'heads' ? 'Heads' : buttonType === 'comments' ? 'Comments' : 'Margin'}Btn`);
        if (button) {
            const isVisible = buttonType === 'heads' ? isHeadsVisible :
                              buttonType === 'comments' ? isCommentsHighlighted :
                              isMarginCommentsVisible;
            const iconColor = isVisible ? '#2eaadc' : '#ff4d4d';
            button.querySelector('svg').style.fill = iconColor;
            button.title = `${isVisible ? 'Hide' : 'Show'} ${buttonType === 'heads' ? 'Floating Heads' : buttonType === 'comments' ? 'Comment Highlights' : 'Margin Comments'}`;
        }
    }

    function createFloatingButtons() {
        const buttonContainer = document.createElement('div');
        buttonContainer.id = 'notionToggleButtonContainer';

        const commentButton = createButton('Comments', 'M20 2H4c-1.1 0-2 .9-2 2v18l4-4h14c1.1 0 2-.9 2-2V4c0-1.1-.9-2-2-2zm0 14H6l-2 2V4h16v12z');
        const headsButton = createButton('Heads', 'M12 2C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2zm0 3c1.66 0 3 1.34 3 3s-1.34 3-3 3-3-1.34-3-3 1.34-3 3-3zm0 14.2c-2.5 0-4.71-1.28-6-3.22.03-1.99 4-3.08 6-3.08 1.99 0 5.97 1.09 6 3.08-1.29 1.94-3.5 3.22-6 3.22z');
        const marginButton = createButton('Margin', 'M21 6h-2v9H6v2c0 .55.45 1 1 1h11l4 4V7c0-.55-.45-1-1-1zm-4 6V3c0-.55-.45-1-1-1H3c-.55 0-1 .45-1 1v14l4-4h10c.55 0 1-.45 1-1z');

        buttonContainer.appendChild(commentButton);
        buttonContainer.appendChild(headsButton);
        buttonContainer.appendChild(marginButton);
        document.body.appendChild(buttonContainer);

        updateButtonAppearance('comments');
        updateButtonAppearance('heads');
        updateButtonAppearance('margin');
    }

    function createButton(type, svgPath) {
        const button = document.createElement('button');
        button.id = `toggle${type}Btn`;
        button.innerHTML = `
            <svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24">
                <path d="${svgPath}"/>
            </svg>
        `;
        button.addEventListener('click', type === 'Comments' ? toggleCommentHighlighting :
                                        type === 'Heads' ? toggleFloatingHeads :
                                        toggleMarginComments);
        return button;
    }

    function init() {
        console.log('Notion Toggles: Script started');
        createFloatingButtons();
        applyInitialState();
        console.log('Notion Toggles: Initial state applied');

        // Set up a MutationObserver to apply our changes when new content is added
        const observer = new MutationObserver(debounce((mutations) => {
            let shouldUpdate = false;
            for (let mutation of mutations) {
                if (mutation.type === 'childList' && mutation.addedNodes.length > 0) {
                    shouldUpdate = true;
                    break;
                }
            }
            if (shouldUpdate) {
                updateFloatingHeadsVisibility();
                updateCommentHighlighting();
                updateMarginCommentsVisibility();
            }
        }, 200));

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

    function applyInitialState() {
        updateFloatingHeadsVisibility();
        updateCommentHighlighting();
        updateMarginCommentsVisibility();
    }

    // Run the script after a short delay to ensure Notion's UI is loaded
    setTimeout(init, 2000);
})();