// ==UserScript== // @name 高级侧边内容查看器 // @namespace http://tampermonkey.net/ // @version 0.5 // @description 点击论坛帖子链接,在优雅的侧边面板中加载内容,支持宽度调整,适配1cili和其他论坛站点 // @author You // @match https://www.tgb.cn/user/blog/* // @match https://www.wnflb2023.com/forum* // @match https://www.52pojie.cn/forum.php?* // @match *://1cili.com/* // @grant GM_xmlhttpRequest // @grant GM_addStyle // @connect * // @license MIT // @downloadURL https://update.greasyfork.icu/scripts/533374/%E9%AB%98%E7%BA%A7%E4%BE%A7%E8%BE%B9%E5%86%85%E5%AE%B9%E6%9F%A5%E7%9C%8B%E5%99%A8.user.js // @updateURL https://update.greasyfork.icu/scripts/533374/%E9%AB%98%E7%BA%A7%E4%BE%A7%E8%BE%B9%E5%86%85%E5%AE%B9%E6%9F%A5%E7%9C%8B%E5%99%A8.meta.js // ==/UserScript== (function() { 'use strict'; // 添加全局样式 GM_addStyle(` /* 基础链接样式优化 - 更柔和的过渡和焦点效果 */ .suh a, table tr td a, th.common a { cursor: pointer; transition: color 0.2s ease-in-out, text-shadow 0.2s ease-in-out; /* 更自然的颜色过渡和阴影 */ text-decoration: none; position: relative; color: inherit; /* 继承父元素的颜色,方便整体风格控制 */ } .suh a:hover, table tr td a:hover, th.common a.xst:hover { color: #2979ff; /* 更现代的蓝色 */ text-shadow: 0 0 5px rgba(41, 121, 255, 0.3); /* 添加轻微的文字阴影 */ } /* 现代化侧边面板样式 - 更精致的边框和阴影 */ #side-content-panel { position: fixed; top: 0; right: -40%; /* 稍微减小初始隐藏宽度 */ width: 40%; /* 相应调整面板宽度 */ height: 100vh; background-color: #fff; border-left: 1px solid #eee; /* 更浅的边框 */ z-index: 10000; overflow-y: auto; box-shadow: -5px 0 30px rgba(0, 0, 0, 0.1); /* 更柔和的阴影 */ transition: right 0.35s cubic-bezier(0.16, 1, 0.3, 1); /* 更流畅的动画 */ display: flex; flex-direction: column; font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif; /* 更现代的字体 */ } #side-content-panel.visible { right: 0; } /* 美化面板头部 - 更清晰的层级和交互反馈 */ #side-panel-header { display: flex; justify-content: space-between; align-items: center; padding: 12px 16px; /* 稍微调整内边距 */ background-color: #fff; /* 与面板背景一致 */ border-bottom: 1px solid #f0f0f0; /* 更柔和的底部边框 */ height: 56px; /* 稍微调整高度 */ min-height: 56px; } #side-panel-title { font-size: 17px; /* 稍微增大字体 */ font-weight: 500; /* 更现代的字重 */ color: #333; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; max-width: calc(100% - 90px); /* 调整最大宽度 */ } #side-panel-actions { display: flex; gap: 8px; /* 稍微调整间距 */ } .side-panel-btn { background-color: #f5f5f5; /* 更浅的按钮背景 */ border: none; border-radius: 4px; /* 更小的圆角 */ padding: 7px 9px; /* 稍微调整内边距 */ cursor: pointer; display: flex; align-items: center; justify-content: center; transition: background-color 0.15s ease-in-out, transform 0.1s ease-in-out, box-shadow 0.15s ease-in-out; /* 更细腻的过渡 */ color: #444; } .side-panel-btn:hover { background-color: #e0e0e0; transform: translateY(-0.5px); /* 更轻微的悬停位移 */ box-shadow: 0 1px 3px rgba(0, 0, 0, 0.08); /* 添加轻微的悬停阴影 */ } .side-panel-btn:active { transform: translateY(0.5px); } #side-panel-close { color: #666; } #side-panel-close:hover { color: #d32f2f; /* 更醒目的关闭颜色 */ } #side-panel-refresh:hover { color: #1976d2; /* 更醒目的刷新颜色 */ } #side-panel-open-in-new:hover { color: #1976d2; /* 更醒目的新窗口颜色 */ } /* 内容区域 - 更平滑的滚动 */ #side-content-area { flex: 1; overflow-y: auto; position: relative; height: calc(100% - 56px); /* 匹配头部高度 */ background-color: #fff; /* 与面板背景一致 */ padding: 15px; /* 添加一些内边距 */ box-sizing: border-box; /* 确保内边距不影响高度计算 */ scroll-behavior: smooth; /* 平滑滚动效果 */ } /* 调整器样式改进 - 更微妙的视觉提示 */ #side-panel-left-resizer { position: absolute; top: 0; left: 0; width: 6px; /* 稍微减小宽度 */ height: 100%; cursor: ew-resize; background-color: transparent; z-index: 10001; transition: background-color 0.15s ease-in-out; /* 更快速的过渡 */ } #side-panel-left-resizer:hover { background-color: rgba(41, 121, 255, 0.1); /* 更柔和的悬停背景 */ } #side-panel-left-resizer:active { background-color: rgba(41, 121, 255, 0.2); } /* 主内容调整器 - 更精致的显示效果 */ #main-content-resizer { position: fixed; top: 0; left: 0; width: 4px; /* 更细的调整器 */ height: 100%; cursor: ew-resize; background-color: #2979ff; /* 更现代的蓝色 */ opacity: 0; z-index: 9999; transition: opacity 0.2s ease-in-out, background-color 0.15s ease-in-out; display: none; } #main-content-resizer:hover { opacity: 0.7 !important; /* 更明显的悬停透明度 */ background-color: #4d90fe; /* 悬停时更亮的蓝色 */ } #main-content-resizer.visible { display: block; opacity: 0.3; /* 初始可见时的透明度 */ } #main-content-resizer:active { opacity: 0.9 !important; } /* 主内容区域调整效果 - 更平滑的过渡 */ body.side-panel-visible { margin-right: 35% !important; /* 匹配面板宽度 */ transition: margin-right 0.35s cubic-bezier(0.16, 1, 0.3, 1); /* 更流畅的动画 */ } /* 加载状态美化 - 更现代的加载动画 */ #side-panel-loading { display: flex; flex-direction: column; align-items: center; justify-content: center; height: 100%; color: #777; /* 更柔和的文字颜色 */ padding: 25px; } .spinner { width: 36px; /* 稍微减小尺寸 */ height: 36px; margin-bottom: 18px; border: 3px solid rgba(41, 121, 255, 0.2); border-radius: 50%; border-top: 3px solid #2979ff; animation: spin 0.8s cubic-bezier(0.68, -0.55, 0.27, 1.55) infinite; /* 更快的动画 */ } @keyframes spin { 0% { transform: rotate(0deg); } 100% { transform: rotate(360deg); } } /* 错误状态美化 - 更清晰的视觉层级 */ #side-panel-error { padding: 35px; color: #c62828; /* 更深的错误颜色 */ text-align: center; display: flex; flex-direction: column; align-items: center; justify-content: center; height: 100%; box-sizing: border-box; } #side-panel-error h3 { margin-top: 18px; margin-bottom: 12px; font-weight: 500; font-size: 1.2em; /* 稍微增大标题 */ } #side-panel-error p { margin-bottom: 22px; color: #777; max-width: 420px; line-height: 1.6; /* 增加行高 */ } #side-panel-error button { padding: 11px 22px; background: #2979ff; color: white; border: none; border-radius: 4px; /* 更小的圆角 */ cursor: pointer; font-weight: 500; transition: background 0.15s ease-in-out, transform 0.1s ease-in-out, box-shadow 0.15s ease-in-out; } #side-panel-error button:hover { background: #4d90fe; transform: translateY(-0.5px); box-shadow: 0 2px 6px rgba(0, 0, 0, 0.15); } #side-panel-error button:active { transform: translateY(0.5px); } /* iframe 样式 - 更柔和的边框 */ #side-panel-iframe { width: 100%; height: 100%; border: none; background-color: white; box-shadow: 0 0 8px rgba(0, 0, 0, 0.05); /* 更柔和的阴影 */ } /* 链接视觉提示优化 - 更精致的图标和动画 */ .side-panel-trigger { position: relative; } .side-panel-trigger::after { content: ''; /* 使用更现代的图标字体,例如 Material Icons */ font-family: 'Material Symbols Outlined'; /* 确保引入了字体 */ position: absolute; right: 8px; /* 稍微调整位置 */ top: 50%; transform: translateY(-50%); font-size: 16px; /* 稍微增大图标 */ opacity: 0; transition: opacity 0.2s ease-in-out, transform 0.15s ease-in-out; /* 更流畅的过渡 */ } .side-panel-trigger:hover::after { opacity: 0.7; transform: translateY(-50%) scale(1.05); /* 更轻微的缩放 */ } /* 论坛帖子标题样式优化 - 同样使用图标字体 */ th.common { position: relative; } th.common::after { content: ''; /* 使用 Material Icons */ font-family: 'Material Symbols Outlined'; position: absolute; right: 8px; top: 50%; transform: translateY(-50%); font-size: 16px; opacity: 0; transition: opacity 0.2s ease-in-out, transform 0.15s ease-in-out; } th.common:hover::after { opacity: 0.7; transform: translateY(-50%) scale(1.05); } /* 美化宽度指示器 - 更现代的样式和动画 */ #width-indicator { position: fixed; bottom: 18px; /* 稍微调整位置 */ right: 18px; background-color: rgba(51, 51, 51, 0.8); /* 更深的背景 */ color: white; padding: 9px 16px; /* 稍微调整内边距 */ border-radius: 22px; /* 更圆润 */ font-size: 13px; /* 稍微增大字体 */ z-index: 10002; pointer-events: none; opacity: 0; transition: opacity 0.25s ease-in-out, transform 0.25s cubic-bezier(0.175, 0.885, 0.32, 1.275); /* 更活泼的动画 */ transform: translateY(15px); font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif; font-weight: 400; /* 更柔和的字重 */ box-shadow: 0 3px 9px rgba(0, 0, 0, 0.25); /* 更明显的阴影 */ } #width-indicator.visible { opacity: 1; transform: translateY(0); } /* 添加遮罩效果 - 更柔和的模糊效果 */ #side-panel-overlay { position: fixed; top: 0; left: 0; width: 100%; height: 100%; background-color: rgba(0, 0, 0, 0.2); /* 更轻的遮罩 */ opacity: 0; z-index: 9998; transition: opacity 0.2s ease-in-out; pointer-events: none; backdrop-filter: blur(3px); /* 更明显的模糊 */ } #side-panel-overlay.visible { opacity: 1; pointer-events: auto; } /* 添加标题栏图标 - 更精致的 SVG 效果 */ #side-panel-title:before { content: ''; display: inline-block; width: 16px; /* 稍微减小尺寸 */ height: 16px; background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%232979ff' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M18 6L6 18M6 6l12 12'%3E%3C/path%3E%3C/svg%3E"); /* 更现代的蓝色 */ background-size: contain; background-repeat: no-repeat; margin-right: 6px; /* 稍微调整间距 */ vertical-align: middle; opacity: 0.7; /* 稍微降低不透明度 */ } /* 美化帖子标题触发效果 - 同样使用图标字体和更精致的动画 */ a.xst { position: relative; display: inline-block; transition: color 0.15s ease-in-out, padding-right 0.15s ease-in-out; /* 同时过渡颜色和内边距 */ padding-right: 20px; /* 稍微调整内边距 */ } a.xst:hover { color: #2979ff; padding-right: 25px; /* 悬停时增加内边距,为图标腾出空间 */ } a.xst::after { content: ''; /* 使用 Material Icons */ font-family: 'Material Symbols Outlined'; position: absolute; right: 0; top: 50%; transform: translateY(-50%); font-size: 16px; opacity: 0; transition: opacity 0.2s ease-in-out, transform 0.15s ease-in-out; } `) // 创建SVG图标 - 使用更高质量的图标 const ICONS = { close: '', external: '', refresh: '', view: '', maximize: '', } // 当前加载的URL let currentUrl = "" // 记录主内容区域的宽度 let mainContentWidth = null // 面板宽度设置 const defaultPanelWidth = "40%" let currentPanelWidth = defaultPanelWidth // 是否为全屏模式 let isFullScreen = false // 创建宽度指示器 function createWidthIndicator() { if (document.getElementById("width-indicator")) { return document.getElementById("width-indicator") } const indicator = document.createElement("div") indicator.id = "width-indicator" document.body.appendChild(indicator) return indicator } // 显示宽度指示器 function showWidthIndicator(width) { const indicator = createWidthIndicator() indicator.textContent = `宽度: ${Math.round(width)}px` indicator.classList.add("visible") // 延迟隐藏 clearTimeout(indicator.hideTimeout) indicator.hideTimeout = setTimeout(() => { indicator.classList.remove("visible") }, 1500) } // 创建遮罩层 function createOverlay() { if (document.getElementById("side-panel-overlay")) { return document.getElementById("side-panel-overlay") } const overlay = document.createElement("div") overlay.id = "side-panel-overlay" overlay.addEventListener("click", closePanel) document.body.appendChild(overlay) return overlay } // 创建右侧内容容器 function createContentPanel() { // 检查是否已存在 if (document.getElementById("side-content-panel")) { return document.getElementById("side-content-panel") } // 创建遮罩层 createOverlay() // 创建侧边栏容器 const sidePanel = document.createElement("div") sidePanel.id = "side-content-panel" sidePanel.style.width = currentPanelWidth // 创建左侧调整大小的元素 const leftResizer = document.createElement("div") leftResizer.id = "side-panel-left-resizer" leftResizer.title = "调整宽度" // 创建头部 const header = document.createElement("div") header.id = "side-panel-header" // 创建标题 const title = document.createElement("div") title.id = "side-panel-title" // 创建按钮区域 const actions = document.createElement("div") actions.id = "side-panel-actions" // 创建刷新按钮 const refreshBtn = document.createElement("button") refreshBtn.id = "side-panel-refresh" refreshBtn.className = "side-panel-btn" refreshBtn.innerHTML = ICONS.refresh refreshBtn.title = "刷新内容" refreshBtn.onclick = () => { if (currentUrl) { loadContent(currentUrl) } } // 创建全屏/恢复按钮 const maximizeBtn = document.createElement("button") maximizeBtn.id = "side-panel-maximize" maximizeBtn.className = "side-panel-btn" maximizeBtn.innerHTML = ICONS.maximize maximizeBtn.title = "全屏/恢复" maximizeBtn.onclick = toggleFullScreen // 创建在新标签页打开按钮 const openBtn = document.createElement("button") openBtn.id = "side-panel-open-in-new" openBtn.className = "side-panel-btn" openBtn.innerHTML = ICONS.external openBtn.title = "在新标签页打开" openBtn.onclick = () => { if (currentUrl) { window.open(currentUrl, "_blank") } } // 创建关闭按钮 const closeBtn = document.createElement("button") closeBtn.id = "side-panel-close" closeBtn.className = "side-panel-btn" closeBtn.innerHTML = ICONS.close closeBtn.title = "关闭" closeBtn.onclick = closePanel // 创建内容区域 const contentArea = document.createElement("div") contentArea.id = "side-content-area" // 组装面板 actions.appendChild(refreshBtn) actions.appendChild(maximizeBtn) actions.appendChild(openBtn) actions.appendChild(closeBtn) header.appendChild(title) header.appendChild(actions) sidePanel.appendChild(leftResizer) sidePanel.appendChild(header) sidePanel.appendChild(contentArea) document.body.appendChild(sidePanel) // 创建主内容区域的调整器 createMainContentResizer() // 添加右侧面板的调整大小功能 initSidePanelResizer(leftResizer, sidePanel) // 添加键盘快捷键 document.addEventListener("keydown", (e) => { if (sidePanel.classList.contains("visible")) { // Escape 关闭面板 if (e.key === "Escape") { closePanel() } // F 全屏/恢复 else if (e.key === "f" || e.key === "F") { toggleFullScreen() } // R 刷新内容 else if (e.key === "r" || e.key === "R") { if (currentUrl) { loadContent(currentUrl) } } } }) // 双击标题栏最大化/还原 header.addEventListener("dblclick", toggleFullScreen) return sidePanel } // 全屏/恢复功能 function toggleFullScreen() { const sidePanel = document.getElementById("side-content-panel") const mainResizer = document.getElementById("main-content-resizer") if (!isFullScreen) { // 保存当前宽度 currentPanelWidth = sidePanel.style.width // 设置全屏 sidePanel.style.width = "100%" sidePanel.style.zIndex = "10002" // 隐藏调整器 mainResizer.classList.remove("visible") isFullScreen = true } else { // 恢复原来的宽度 sidePanel.style.width = currentPanelWidth sidePanel.style.zIndex = "10000" // 显示调整器 mainResizer.classList.add("visible") isFullScreen = false } } // 创建主内容区域的调整器 function createMainContentResizer() { if (document.getElementById("main-content-resizer")) { return document.getElementById("main-content-resizer") } const resizer = document.createElement("div") resizer.id = "main-content-resizer" resizer.title = "调整内容区域宽度" document.body.appendChild(resizer) // 初始化主内容调整功能 initMainContentResizer(resizer) return resizer } // 初始化主内容区域的调整大小功能 function initMainContentResizer(resizer) { let startX, startWidth resizer.addEventListener("mousedown", (e) => { e.preventDefault() e.stopPropagation() startX = e.clientX // 获取当前内容宽度 const computedStyle = window.getComputedStyle(document.body) const paddingLeft = Number.parseFloat(computedStyle.paddingLeft) || 0 const marginLeft = Number.parseFloat(computedStyle.marginLeft) || 0 startWidth = mainContentWidth || paddingLeft + marginLeft document.documentElement.style.cursor = "ew-resize" document.addEventListener("mousemove", resizeMainContent) document.addEventListener("mouseup", stopResizeMainContent) // 高亮调整器 resizer.style.opacity = "0.9" }) function resizeMainContent(e) { const width = startWidth + (e.clientX - startX) if (width >= 0 && width <= window.innerWidth * 0.6) { document.body.style.marginLeft = width + "px" resizer.style.left = width + "px" mainContentWidth = width showWidthIndicator(width) } } function stopResizeMainContent() { document.documentElement.style.cursor = "" document.removeEventListener("mousemove", resizeMainContent) document.removeEventListener("mouseup", stopResizeMainContent) // 恢复调整器透明度 resizer.style.opacity = "0.5" } } // 初始化右侧面板的调整大小功能 function initSidePanelResizer(resizer, panel) { let startX, startWidth, minWidth = 300, maxWidth resizer.addEventListener("mousedown", (e) => { if (isFullScreen) return // 全屏模式下禁用调整大小 e.preventDefault() e.stopPropagation() startX = e.clientX startWidth = Number.parseInt(panel.offsetWidth) maxWidth = window.innerWidth * 0.9 // 最大宽度为窗口的90% document.documentElement.style.cursor = "ew-resize" document.addEventListener("mousemove", resizeSidePanel) document.addEventListener("mouseup", stopResizeSidePanel) // 高亮调整器 resizer.style.backgroundColor = "rgba(66, 133, 244, 0.25)" }) function resizeSidePanel(e) { const width = startWidth - (e.clientX - startX) if (width >= minWidth && width <= maxWidth) { panel.style.width = width + "px" currentPanelWidth = width + "px" // 保存当前宽度 showWidthIndicator(width) } } function stopResizeSidePanel() { document.documentElement.style.cursor = "" document.removeEventListener("mousemove", resizeSidePanel) document.removeEventListener("mouseup", stopResizeSidePanel) // 恢复调整器外观 resizer.style.backgroundColor = "transparent" } } // 关闭面板 function closePanel() { const sidePanel = document.getElementById("side-content-panel") const overlay = document.getElementById("side-panel-overlay") if (sidePanel) { sidePanel.classList.remove("visible") document.body.classList.remove("side-panel-visible") // 隐藏遮罩 if (overlay) { overlay.classList.remove("visible") } // 隐藏主内容调整器 const mainResizer = document.getElementById("main-content-resizer") if (mainResizer) { mainResizer.classList.remove("visible") } // 如果在全屏模式,恢复原来的宽度 if (isFullScreen) { sidePanel.style.width = currentPanelWidth sidePanel.style.zIndex = "10000" isFullScreen = false } // 延迟清空内容,等动画完成 setTimeout(() => { const contentArea = document.getElementById("side-content-area") if (contentArea) { contentArea.innerHTML = "" } }, 400) } } // 显示面板 function showPanel(title, url) { const sidePanel = createContentPanel() const overlay = document.getElementById("side-panel-overlay") currentUrl = url // 设置标题 const titleElement = document.getElementById("side-panel-title") titleElement.textContent = title || "查看内容" // 显示面板 sidePanel.classList.add("visible") document.body.classList.add("side-panel-visible") // 显示遮罩 if (overlay) { overlay.classList.add("visible") } // 显示主内容调整器 const mainResizer = document.getElementById("main-content-resizer") if (mainResizer && !isFullScreen) { // 根据当前主内容区域的左边距设置位置 const marginLeft = Number.parseFloat(window.getComputedStyle(document.body).marginLeft) || 0 mainResizer.style.left = marginLeft + "px" mainResizer.classList.add("visible") } return document.getElementById("side-content-area") } // 显示加载中 function showLoading(container) { container.innerHTML = `
\{message \|\| "无法加载请求的内容"\}