From ddbdfb723636545f11a62de914b06f4f26913fc4 Mon Sep 17 00:00:00 2001 From: Charliechen114514 <725610365@qq.com> Date: Mon, 29 Jun 2026 00:13:33 +0800 Subject: [PATCH] feat: site update, dragable sidebars --- site/.vitepress/config/index.ts | 6 + site/.vitepress/config/shared.ts | 6 + .../theme/components/ResizableSidebar.vue | 177 ++++++++++++++++++ site/.vitepress/theme/custom.css | 83 ++++++++ site/.vitepress/theme/index.ts | 2 + 5 files changed, 274 insertions(+) create mode 100644 site/.vitepress/theme/components/ResizableSidebar.vue diff --git a/site/.vitepress/config/index.ts b/site/.vitepress/config/index.ts index abe38149f..1ccbd485e 100644 --- a/site/.vitepress/config/index.ts +++ b/site/.vitepress/config/index.ts @@ -89,6 +89,12 @@ export default withDrawio(defineConfig({ {}, `(function(){try{var s=localStorage.getItem('vp-font-size')||'normal';if(s!=='xxsmall'&&s!=='small'&&s!=='normal'&&s!=='large'&&s!=='xxlarge'){s='normal';}document.documentElement.dataset.fontSize=s;}catch(e){}})()`, ], + // 首屏立即应用侧栏宽度(左导航 + 右大纲),防刷新闪烁。key 与 ResizableSidebar.vue 一致。 + [ + 'script', + {}, + `(function(){try{var w=parseInt(localStorage.getItem('vp-sidebar-width'));if(!w||w<200||w>480){w=272;}document.documentElement.style.setProperty('--vp-sidebar-width',w+'px');var a=parseInt(localStorage.getItem('vp-aside-width'));if(!a||a<180||a>360){a=256;}document.documentElement.style.setProperty('--vp-aside-width',a+'px');}catch(e){}})()`, + ], ], markdown: sharedMarkdown, diff --git a/site/.vitepress/config/shared.ts b/site/.vitepress/config/shared.ts index 53ba0495a..3652b9045 100644 --- a/site/.vitepress/config/shared.ts +++ b/site/.vitepress/config/shared.ts @@ -62,6 +62,12 @@ export const sharedBase = { {}, `(function(){try{var s=localStorage.getItem('vp-font-size')||'normal';if(s!=='xxsmall'&&s!=='small'&&s!=='normal'&&s!=='large'&&s!=='xxlarge'){s='normal';}document.documentElement.dataset.fontSize=s;}catch(e){}})()`, ], + // 首屏立即应用侧栏宽度(左导航 + 右大纲),防刷新闪烁。key 与 ResizableSidebar.vue 一致。 + [ + 'script', + {}, + `(function(){try{var w=parseInt(localStorage.getItem('vp-sidebar-width'));if(!w||w<200||w>480){w=272;}document.documentElement.style.setProperty('--vp-sidebar-width',w+'px');var a=parseInt(localStorage.getItem('vp-aside-width'));if(!a||a<180||a>360){a=256;}document.documentElement.style.setProperty('--vp-aside-width',a+'px');}catch(e){}})()`, + ], ], markdown: sharedMarkdown, diff --git a/site/.vitepress/theme/components/ResizableSidebar.vue b/site/.vitepress/theme/components/ResizableSidebar.vue new file mode 100644 index 000000000..aeaf3606d --- /dev/null +++ b/site/.vitepress/theme/components/ResizableSidebar.vue @@ -0,0 +1,177 @@ + + + diff --git a/site/.vitepress/theme/custom.css b/site/.vitepress/theme/custom.css index 1ebd59dfc..ac658e1a5 100644 --- a/site/.vitepress/theme/custom.css +++ b/site/.vitepress/theme/custom.css @@ -1252,6 +1252,89 @@ html.dark .online-compiler-demo__source-highlight .shiki span { font-size: 0.85em; } +/* ================================================================ + Resizable Sidebar(可拖拽侧栏宽度) + 组件: theme/components/ResizableSidebar.vue + 左 handle 在组件模板内(fixed),右 handle 运行时注入 .aside(absolute)。 + 宽度由 --vp-sidebar-width(VitePress 原生,全链路消费)与 --vp-aside-width(本站自定义)驱动。 + ================================================================ */ + +:root { + --vp-aside-width: 256px; /* 右大纲栏宽度变量,默认与 VitePress 原值一致 */ +} + +/* 右栏宽度由变量驱动,覆盖 VitePress 的固定 max-width / width */ +.VPDoc.has-aside .aside { + max-width: var(--vp-aside-width); + position: relative; /* 给注入的 absolute handle 提供锚点 */ +} +.VPDoc .aside-container { + width: calc(var(--vp-aside-width) - 32px); /* 保持原 32px 内边距逻辑(原 max-width 256 → container 224) */ +} + +/* 手柄公共态:常显一条 1px 淡灰细线(::before),悬浮 / 拖拽时变品牌色加粗。 + 命中热区 8px(透明本体),避免过窄扫不到。z-index 压在 sidebar 之上,防止被侧栏盖住。 */ +.rs-handle { + z-index: calc(var(--vp-z-index-sidebar) + 1); + width: 8px; + background: transparent; + cursor: col-resize; +} +.rs-handle::before { + content: ''; + position: absolute; + left: 50%; + top: 0; + bottom: 0; + width: 1px; + transform: translateX(-50%); + background: var(--vp-c-divider); + transition: width 0.15s ease, background-color 0.15s ease; +} +.rs-handle:hover::before, +.rs-handle.is-active::before { + width: 2px; + background: var(--vp-c-brand-1); +} + +/* 左手柄:fixed。left 仅作 SSR 首屏 fallback,onMounted 后由 JS 读取 .VPSidebar 右边缘精确 + 覆盖 —— 宽屏(≥1440px)布局居中,右缘带 (vw-maxW)/2 偏移且受滚动条影响,CSS 公式推算 + 总有小偏差,改读 getBoundingClientRect 根治(与右 handle 同策略)。 */ +.rs-handle--left { + position: fixed; + left: var(--vp-sidebar-width); + margin-left: -4px; + top: var(--vp-nav-height, 64px); + bottom: 0; +} + +/* 右手柄:absolute 注入 .aside 内,居中压在其左边缘(默认隐藏,仅大屏显示见下方媒体查询) */ +.rs-handle--right { + position: absolute; + left: -4px; + top: 0; + bottom: 0; + display: none; +} + +/* 拖拽进行中:禁选中文,全屏 col-resize 光标 */ +body.rs-resizing { + user-select: none; + cursor: col-resize; +} + +/* 响应式:左栏 <960px 沉为抽屉;右栏 aside 仅 ≥1280px 显示 */ +@media (max-width: 959px) { + .rs-handle--left { + display: none; + } +} +@media (min-width: 1280px) { + .rs-handle--right { + display: block; + } +} + /* ================================================================ Content Page Enhancements ================================================================ */ diff --git a/site/.vitepress/theme/index.ts b/site/.vitepress/theme/index.ts index c3658a2ef..2931fe0f6 100644 --- a/site/.vitepress/theme/index.ts +++ b/site/.vitepress/theme/index.ts @@ -14,6 +14,7 @@ import HomeHeroVisual from './components/HomeHeroVisual.vue' import ProofStrip from './components/ProofStrip.vue' import HomeRoadmap from './components/HomeRoadmap.vue' import FontSizeSwitcher from './components/FontSizeSwitcher.vue' +import ResizableSidebar from './components/ResizableSidebar.vue' import { setupMermaid } from './mermaid-client' import './custom.css' @@ -21,6 +22,7 @@ export default { extends: DefaultTheme, Layout() { return h(DefaultTheme.Layout, null, { + 'layout-top': () => h(ResizableSidebar), 'home-hero-image': () => h(HomeHeroVisual), 'home-hero-actions-after': () => h('div', { class: 'proof-on-mobile' }, [h(ProofStrip)]), 'home-hero-after': () => h('div', { class: 'proof-on-desktop' }, [h(ProofStrip)]),