// ==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 // @downloadURL 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(); } })();