1+ {% extends "!layout.html" %}
2+
3+ {% block menu %}
4+ {% if is_independent %}
5+ <!-- 1. 将原本庞大的全局菜单隐藏起来,作为数据源 -->
6+ < div id ="temp-menu-holder " style ="display: none; ">
7+ {{ super() }}
8+ </ div >
9+
10+ <!-- 2. 独立站点独立侧边栏容器(直接插入在原菜单位置) -->
11+ < div id ="independent-sidebar "> </ div >
12+
13+ < script >
14+ document . addEventListener ( 'DOMContentLoaded' , function ( ) {
15+ const mapping = { { comm_config . get ( 'sidebar_mapping' , { } ) | tojson | safe } } ;
16+ const commId = '{{ current_community_id }}' ;
17+
18+ // 获取数据源和目标容器
19+ const globalMenu = document . getElementById ( 'temp-menu-holder' ) ;
20+ const targetContainer = document . getElementById ( 'independent-sidebar' ) ;
21+
22+ if ( ! globalMenu ) return ;
23+
24+ // 步骤 A: 找到当前社区的主节点 (通常带有 .current 类)
25+ let activeL1 = globalMenu . querySelector ( 'li.toctree-l1.current' ) ;
26+
27+ // 兜底策略: 如果没有 .current (如强制刷新等边缘情况),通过链接匹配
28+ if ( ! activeL1 || ! activeL1 . querySelector ( 'a' ) . href . includes ( '/sources/' + commId + '/' ) ) {
29+ const links = globalMenu . querySelectorAll ( 'li.toctree-l1 > a' ) ;
30+ for ( let i = 0 ; i < links . length ; i ++ ) {
31+ if ( links [ i ] . href . includes ( '/sources/' + commId + '/' ) ) {
32+ activeL1 = links [ i ] . closest ( 'li' ) ;
33+ break ;
34+ }
35+ }
36+ }
37+
38+ if ( activeL1 ) {
39+ // 步骤 B: 找到属于这个社区的所有子页面 (<ul>)
40+ const subMenu = activeL1 . querySelector ( 'ul' ) ;
41+
42+ if ( subMenu && subMenu . children . length > 0 ) {
43+ let currentGroup = null ;
44+ let currentUl = null ;
45+
46+ // 步骤 C: 遍历所有的子页面,将它们分组并插入到独立侧边栏中
47+ const items = Array . from ( subMenu . children ) ;
48+ items . forEach ( item => {
49+ if ( item . tagName . toLowerCase ( ) !== 'li' ) return ;
50+
51+ const link = item . querySelector ( 'a' ) ;
52+ if ( ! link ) return ;
53+
54+ const fullUrl = link . href ;
55+ let matchedFolder = null ;
56+
57+ // 匹配 conf.py 中配置的文件夹路径
58+ for ( const folder in mapping ) {
59+ if ( fullUrl . includes ( '/' + folder + '/' ) ) {
60+ matchedFolder = folder ;
61+ break ;
62+ }
63+ }
64+
65+ // 如果有的文件放在根目录没匹配到,归入 default
66+ if ( ! matchedFolder ) {
67+ matchedFolder = currentGroup || 'default' ;
68+ }
69+
70+ // 步骤 D: 遇到新分组时,创建大标题和新的 <ul>
71+ if ( currentGroup !== matchedFolder ) {
72+ if ( mapping [ matchedFolder ] ) {
73+ const caption = document . createElement ( 'p' ) ;
74+ caption . className = 'caption custom-sidebar-caption' ;
75+ caption . innerHTML = `<span class="caption-text">${ mapping [ matchedFolder ] } </span>` ;
76+ targetContainer . appendChild ( caption ) ;
77+ }
78+
79+ currentUl = document . createElement ( 'ul' ) ;
80+ targetContainer . appendChild ( currentUl ) ;
81+ currentGroup = matchedFolder ;
82+ }
83+
84+ // 步骤 E: 智能动态提升整棵树的级数,完美支持长文档的内部 H2/H3 目录显示
85+ // 找出该节点及其内部所有的级联列表项
86+ const allTocItems = [ item , ...item . querySelectorAll ( '[class*="toctree-l"]' ) ] ;
87+ allTocItems . forEach ( el => {
88+ // 将 toctree-l(X) 动态替换为 toctree-l(X-1)
89+ el . className = el . className . replace ( / t o c t r e e - l ( \d + ) / g, function ( match , level ) {
90+ const newLevel = parseInt ( level , 10 ) - 1 ;
91+ return 'toctree-l' + newLevel ;
92+ } ) ;
93+ } ) ;
94+
95+ // 挂载到对应独立社区的菜单中
96+ if ( currentUl ) {
97+ currentUl . appendChild ( item ) ;
98+ }
99+ } ) ;
100+
101+ // 步骤 F: 彻底删除隐蔽的全局菜单
102+ globalMenu . remove ( ) ;
103+ return ;
104+ }
105+ }
106+
107+ // 异常兜底: 如果出现极端的解析失败(比如网络延迟),取消隐藏,确保文档菜单可用
108+ globalMenu . style . display = 'block' ;
109+ } ) ;
110+ </ script >
111+ {% else %}
112+ <!-- 非独立社区页面,直接按普通 Sphinx 逻辑渲染 -->
113+ {{ super() }}
114+ {% endif %}
115+ {% endblock %}
0 commit comments