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+ // 使用更精确的匹配:sources/{commId}/{folder}/ 或 sources/_generated/sources/{commId}/{folder}/
59+ for ( const folder in mapping ) {
60+ const pattern1 = `/sources/${ commId } /${ folder } /` ;
61+ const pattern2 = `/sources/_generated/sources/${ commId } /${ folder } /` ;
62+ if ( fullUrl . includes ( pattern1 ) || fullUrl . includes ( pattern2 ) ) {
63+ matchedFolder = folder ;
64+ break ;
65+ }
66+ }
67+
68+ // 如果有的文件放在根目录没匹配到,归入 default
69+ if ( ! matchedFolder ) {
70+ matchedFolder = currentGroup || 'default' ;
71+ }
72+
73+ // 步骤 D: 遇到新分组时,创建大标题和新的 <ul>
74+ if ( currentGroup !== matchedFolder ) {
75+ if ( mapping [ matchedFolder ] ) {
76+ const caption = document . createElement ( 'p' ) ;
77+ caption . className = 'caption custom-sidebar-caption' ;
78+ caption . innerHTML = `<span class="caption-text">${ mapping [ matchedFolder ] } </span>` ;
79+ targetContainer . appendChild ( caption ) ;
80+ }
81+
82+ currentUl = document . createElement ( 'ul' ) ;
83+ targetContainer . appendChild ( currentUl ) ;
84+ currentGroup = matchedFolder ;
85+ }
86+
87+ // 步骤 E: 智能动态提升整棵树的级数,完美支持长文档的内部 H2/H3 目录显示
88+ // 找出该节点及其内部所有的级联列表项
89+ const allTocItems = [ item , ...item . querySelectorAll ( '[class*="toctree-l"]' ) ] ;
90+ allTocItems . forEach ( el => {
91+ // 将 toctree-l(X) 动态替换为 toctree-l(X-1)
92+ el . className = el . className . replace ( / t o c t r e e - l ( \d + ) / g, function ( match , level ) {
93+ const newLevel = parseInt ( level , 10 ) - 1 ;
94+ return 'toctree-l' + newLevel ;
95+ } ) ;
96+ } ) ;
97+
98+ // 挂载到对应独立社区的菜单中
99+ if ( currentUl ) {
100+ currentUl . appendChild ( item ) ;
101+ }
102+ } ) ;
103+
104+ // 步骤 F: 彻底删除隐蔽的全局菜单
105+ globalMenu . remove ( ) ;
106+ return ;
107+ }
108+ }
109+
110+ // 异常兜底: 如果出现极端的解析失败(比如网络延迟),取消隐藏,确保文档菜单可用
111+ globalMenu . style . display = 'block' ;
112+ } ) ;
113+ </ script >
114+ {% else %}
115+ <!-- 非独立社区页面,直接按普通 Sphinx 逻辑渲染 -->
116+ {{ super() }}
117+ {% endif %}
118+ {% endblock %}
0 commit comments