Greasy Fork

아카라이브 듀얼스크린

아카라이브의 게시글을 게시글과 게시글 목록으로 나누어 듀얼스크린으로 변경합니다.

目前为 2025-01-02 提交的版本。查看 最新版本

// ==UserScript==
// @name         아카라이브 듀얼스크린
// @namespace    http://tampermonkey.net/
// @version      1.0.1
// @description  아카라이브의 게시글을 게시글과 게시글 목록으로 나누어 듀얼스크린으로 변경합니다.
// @icon         https://www.google.com/s2/favicons?sz=64&domain=arca.live
// @author       스토커
// @match        *://*.arca.live/b/*/*
// @exclude      *://*.arca.live/b/*/write
// @grant        none
// ==/UserScript==

(function () {
    'use strict';

    const style = document.createElement('style');
    style.textContent = `
        body {
            margin: 0;
            padding: 0;
            overflow: hidden;
            background: none !important;
        }

        html, html.theme-light {
            background: none !important;
        }

        .dual-screen-container {
            position: fixed;
            top: 0;
            left: 0;
            right: 0;
            bottom: 0;
            display: flex;
            width: 100%;
            margin: 0;
            padding: 0;
            background: #fff;
            border: none;
            height: 100vh;
            min-height: 100vh;
            position: fixed;
            overflow: hidden;
        }

        .left-panel, .right-panel {
            height: 100%;
            position: relative;
            background: #fff;
        }

        .left-panel {
            min-width: 200px;
            flex: 2;
            padding: 0;
            border-right: 1px solid #ddd;
            overflow-y: auto;
            overscroll-behavior-y: contain !important;
        }

        .left-panel .content-container {
            display: flex;
            flex-direction: column;
        }

        .left-panel .navbar {
            position: relative !important;
            width: 100%;
            background: #4f5464 !important;
            z-index: 100;
            color: #fff !important;
        }

        .left-panel .navbar .nav-link {
            color: #fff !important;
        }

        .user-dropdown-menu {
            left: -50% !important;
        }

        .noti-dropdown-menu{
            left: -50% !important;
        }

        .left-panel .board-title {
            position: relative !important;
            width: 100%;
            background: #fff;
            margin: 0em 0rem 0rem 0rem !important;
            padding: 10px;
            border-bottom: 1px solid #ddd;
            z-index: 99;
        }

        .left-panel .article-body {
            margin-top: 0;
            padding: 10px;
        }

        .left-panel .navbar .navbar-brand svg {
            width: 21px;
            height: 21px;
        }

        .right-panel {
            min-width: 200px;
            flex: 1;
            display: flex;
            flex-direction: column;
        }

        .right-panel .right-sidebar {
            padding: 10px;
            border-bottom: 1px solid #ddd;
            margin: 0;
        }

        .right-panel .included-article-list-wrapper {
            flex: 1;
            overflow-y: auto;
            padding: 0px;
            overscroll-behavior: contain;
            touch-action: pan-y;
        }

        .right-panel .footer {
            padding: 10px;
            border-top: 1px solid #ddd;
        }

        .resize-handle {
            width: 6px;
            background: #ddd;
            cursor: col-resize;
            transition: background 0.3s;
            position: relative;
            min-height: 100vh;
            height: 100%;
            flex-shrink: 0;
            z-index: 1000;
        }

        .resize-handle:hover {
            background: #999;
        }

        .resize-handle.dragging {
            background: #666;
        }

        .reply-form__user-info__avatar{
            width: 1.4em !important;
        }

        .board-category-wrapper {
            overflow-x: auto !important;
            white-space: nowrap !important;
            -webkit-overflow-scrolling: touch !important;
            scrollbar-width: none !important;
            overscroll-behavior-x: contain !important;
            overscroll-behavior-y: none !important;
            position: relative !important;
            z-index: 2 !important;
            touch-action: pan-x !important;
        }

        .board-category-wrapper::-webkit-scrollbar {
            display: none !important;
        }

        .board-category {
            display: flex !important;
            flex-wrap: nowrap !important;
            padding-bottom: 5px !important;
            -webkit-user-select: none !important;
            user-select: none !important;
            touch-action: pan-x !important;
        }

    `;
    document.head.appendChild(style);

    const Settings = {
        storageKey: 'arcalive_dualscreen_settings',
        defaults: {
            isSwapped: false,
            leftPanelWidth: '66.66%',
            rightPanelWidth: '33.33%'
        },

        load() {
            try {
                const saved = localStorage.getItem(this.storageKey);
                if (!saved) {
                    return this.defaults;
                }
                const parsed = JSON.parse(saved);
                return {
                    ...this.defaults,
                    ...parsed
                };
            } catch (e) {
                console.error('설정 로드 실패:', e);
                return this.defaults;
            }
        },

        save(settings) {
            try {
                const saveData = {
                    isSwapped: settings.isSwapped,
                    leftPanelWidth: typeof settings.leftPanelWidth === 'number' ?
                        settings.leftPanelWidth + '%' : settings.leftPanelWidth,
                    rightPanelWidth: typeof settings.rightPanelWidth === 'number' ?
                        settings.rightPanelWidth + '%' : settings.rightPanelWidth
                };
                localStorage.setItem(this.storageKey, JSON.stringify(saveData));
            } catch (e) {
                console.error('설정 저장 실패:', e);
            }
        },

        saveCurrentLayout(isSwapped, leftPanel, rightPanel) {
            const totalWidth = leftPanel.parentElement.offsetWidth - 6;
            const leftWidth = (leftPanel.offsetWidth / totalWidth) * 100;
            const rightWidth = (rightPanel.offsetWidth / totalWidth) * 100;

            this.save({
                isSwapped: isSwapped,
                leftPanelWidth: leftWidth,
                rightPanelWidth: rightWidth
            });
        }
    };

    function initializeDualScreen() {
        const navbar = document.querySelector('.navbar');
        const boardTitle = document.querySelector('.board-title');
        const articleWrapper = document.querySelector('.article-wrapper');
        const includedArticles = document.querySelector('.included-article-list');
        const rightSidebar = document.querySelector('.right-sidebar');
        const footer = document.querySelector('.footer');

        if (!articleWrapper || !includedArticles) return;

        const container = document.createElement('div');
        container.className = 'dual-screen-container';

        const leftPanel = document.createElement('div');
        leftPanel.className = 'left-panel';

        const contentContainer = document.createElement('div');
        contentContainer.className = 'content-container';

        if (navbar) {
            const navbarClone = navbar.cloneNode(true);
            contentContainer.appendChild(navbarClone);
        }

        if (boardTitle) {
            const boardTitleClone = boardTitle.cloneNode(true);
            contentContainer.appendChild(boardTitleClone);
        }

        const articleBody = document.createElement('div');
        articleBody.className = 'article-body';
        articleBody.appendChild(articleWrapper.cloneNode(true));
        contentContainer.appendChild(articleBody);

        leftPanel.appendChild(contentContainer);

        const resizeHandle = document.createElement('div');
        resizeHandle.className = 'resize-handle';

        const rightPanel = document.createElement('div');
        rightPanel.className = 'right-panel';

        if (rightSidebar) {
            rightPanel.appendChild(rightSidebar);
        }

        const includedArticlesWrapper = document.createElement('div');
        includedArticlesWrapper.className = 'included-article-list-wrapper';
        includedArticlesWrapper.appendChild(includedArticles.cloneNode(true));
        rightPanel.appendChild(includedArticlesWrapper);

        if (footer) {
            rightPanel.appendChild(footer);
        }

        container.appendChild(leftPanel);
        container.appendChild(resizeHandle);
        container.appendChild(rightPanel);

        const settings = Settings.load();
        let isPanelsSwapped = settings.isSwapped;

        const applyLayout = () => {
            const settings = Settings.load();

            leftPanel.style.width = settings.leftPanelWidth;
            leftPanel.style.flex = 'none';
            rightPanel.style.width = settings.rightPanelWidth;
            rightPanel.style.flex = 'none';

            while (container.firstChild) {
                container.removeChild(container.firstChild);
            }

            if (isPanelsSwapped) {
                container.appendChild(rightPanel);
                container.appendChild(resizeHandle);
                container.appendChild(leftPanel);
            } else {
                container.appendChild(leftPanel);
                container.appendChild(resizeHandle);
                container.appendChild(rightPanel);
            }
        };

        applyLayout();

        const swapPanels = () => {
            isPanelsSwapped = !isPanelsSwapped;

            const totalWidth = container.offsetWidth - 6;
            const biggerWidth = Math.max(leftPanel.offsetWidth, rightPanel.offsetWidth);
            const smallerWidth = Math.min(leftPanel.offsetWidth, rightPanel.offsetWidth);

            const biggerRatio = (biggerWidth / totalWidth) * 100;
            const smallerRatio = (smallerWidth / totalWidth) * 100;

            const isLeftBigger = leftPanel.offsetWidth > rightPanel.offsetWidth;

            if (isLeftBigger) {
                leftPanel.style.width = `${smallerRatio}%`;
                rightPanel.style.width = `${biggerRatio}%`;
            } else {
                leftPanel.style.width = `${biggerRatio}%`;
                rightPanel.style.width = `${smallerRatio}%`;
            }

            applyLayout();
            Settings.saveCurrentLayout(isPanelsSwapped, leftPanel, rightPanel);
        };

        resizeHandle.addEventListener('dblclick', swapPanels);

        articleWrapper.parentNode.replaceChild(container, articleWrapper);
        includedArticles.remove();
        if (rightSidebar) rightSidebar.remove();
        if (footer) footer.remove();
        if (navbar) navbar.remove();
        if (boardTitle) boardTitle.remove();

        let isResizing = false;
        let startX, startWidth;

        const handleResize = (e) => {
            if (!isResizing) return;

            const containerWidth = container.offsetWidth;
            const minWidth = 200;
            const maxWidth = containerWidth - minWidth;

            const currentX = e.pageX;
            const diffX = currentX - startX;
            const targetPanel = isPanelsSwapped ? rightPanel : leftPanel;
            const otherPanel = isPanelsSwapped ? leftPanel : rightPanel;

            let newWidth = startWidth + diffX;
            newWidth = Math.max(minWidth, Math.min(maxWidth, newWidth));
            const otherWidth = containerWidth - newWidth - 6;

            targetPanel.style.width = `${newWidth}px`;
            targetPanel.style.flex = 'none';
            otherPanel.style.width = `${otherWidth}px`;
            otherPanel.style.flex = 'none';

            const totalWidth = container.offsetWidth - 6;
            const leftWidth = parseFloat((leftPanel.offsetWidth / totalWidth) * 100).toFixed(2);
            const rightWidth = parseFloat((rightPanel.offsetWidth / totalWidth) * 100).toFixed(2);

            Settings.save({
                isSwapped: isPanelsSwapped,
                leftPanelWidth: leftWidth + '%',
                rightPanelWidth: rightWidth + '%'
            });
        };

        resizeHandle.addEventListener('mousedown', (e) => {
            isResizing = true;
            resizeHandle.classList.add('dragging');
            startX = e.pageX;
            startWidth = (isPanelsSwapped ? rightPanel : leftPanel).offsetWidth;
        });

        document.addEventListener('mousemove', handleResize);

        document.addEventListener('mouseup', () => {
            if (isResizing) {
                isResizing = false;
                resizeHandle.classList.remove('dragging');
            }
        });

        let resizeTimeout;
        window.addEventListener('resize', () => {
            clearTimeout(resizeTimeout);
            resizeTimeout = setTimeout(applyLayout, 100);
        });
    }

    if (document.readyState === 'loading') {
        document.addEventListener('DOMContentLoaded', initializeDualScreen);
    } else {
        initializeDualScreen();
    }
})();