Greasy Fork is available in English.
아카라이브의 게시글을 게시글과 게시글 목록으로 나누어 듀얼스크린으로 변경합니다.
当前为
// ==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();
}
})();