@@ -5,6 +5,10 @@ window.$docsify = {
55 // 文档内容与侧边栏都存放在 docs/ 下
66 basePath : 'docs/' , // 所有 Markdown 路由以 docs/ 为前缀
77 loadSidebar : '_sidebar.md' , // 在 basePath 下加载 _sidebar.md
8+ // 始终使用根目录的 _sidebar.md,避免每个子目录都要放一份
9+ alias : {
10+ '/.*/_sidebar.md' : '/_sidebar.md' ,
11+ } ,
812 subMaxLevel : 2 ,
913
1014 // --- 核心:注册自定义插件 ---
@@ -197,7 +201,141 @@ window.$docsify = {
197201 document . head . appendChild ( meta ) ;
198202 } ;
199203
200- // 2. 渲染评论区的 HTML 结构
204+ // 3. 侧边栏按“日期”折叠的辅助函数
205+ const setupCollapsibleSidebarByDay = ( ) => {
206+ const nav = document . querySelector ( '.sidebar-nav' ) ;
207+ if ( ! nav ) return ;
208+
209+ const STORAGE_KEY = 'dpr_sidebar_day_state_v1' ;
210+ let state = { } ;
211+ try {
212+ const raw = window . localStorage
213+ ? window . localStorage . getItem ( STORAGE_KEY )
214+ : null ;
215+ if ( raw ) {
216+ state = JSON . parse ( raw ) || { } ;
217+ }
218+ } catch {
219+ state = { } ;
220+ }
221+ // 先扫描一遍,找出所有日期和最新一天
222+ const items = nav . querySelectorAll ( 'li' ) ;
223+ const dayItems = [ ] ;
224+ let latestDay = '' ;
225+
226+ items . forEach ( ( li ) => {
227+ if ( li . dataset . dayToggleApplied === '1' ) return ;
228+
229+ const childUl = li . querySelector ( ':scope > ul' ) ;
230+ const directLink = li . querySelector ( ':scope > a' ) ;
231+ if ( ! childUl || directLink ) return ;
232+
233+ // 取第一个文本节点作为标签文本
234+ const first = li . firstChild ;
235+ if ( ! first || first . nodeType !== Node . TEXT_NODE ) return ;
236+ const rawText = ( first . textContent || '' ) . trim ( ) ;
237+ if ( ! / ^ \d { 4 } - \d { 2 } - \d { 2 } $ / . test ( rawText ) ) return ;
238+
239+ dayItems . push ( { li, text : rawText , first } ) ;
240+ if ( ! latestDay || rawText > latestDay ) {
241+ latestDay = rawText ;
242+ }
243+ } ) ;
244+
245+ if ( ! dayItems . length ) return ;
246+
247+ // 判断是否出现了“更新后的新一天”
248+ const prevLatest =
249+ typeof state . __latestDay === 'string' ? state . __latestDay : null ;
250+ const isNewDay =
251+ latestDay &&
252+ ( ! prevLatest || ( typeof prevLatest === 'string' && latestDay > prevLatest ) ) ;
253+
254+ // 如果出现了新的一天:清空历史状态,只保留最新一天的信息
255+ if ( isNewDay ) {
256+ state = { __latestDay : latestDay } ;
257+ } else if ( ! prevLatest && latestDay ) {
258+ // 第一次使用,没有历史记录但也不算“新一天触发重置”的场景:记录当前最新日期
259+ state . __latestDay = latestDay ;
260+ }
261+
262+ const hasAnyState =
263+ ! isNewDay && Object . keys ( state ) . some ( ( k ) => k !== '__latestDay' ) ;
264+
265+ const ensureStateSaved = ( ) => {
266+ try {
267+ if ( window . localStorage ) {
268+ window . localStorage . setItem ( STORAGE_KEY , JSON . stringify ( state ) ) ;
269+ }
270+ } catch {
271+ // ignore
272+ }
273+ } ;
274+
275+ // 第二遍:真正安装折叠行为
276+ dayItems . forEach ( ( { li, text : rawText , first } ) => {
277+ if ( li . dataset . dayToggleApplied === '1' ) return ;
278+
279+ // 创建可点击的容器(包含日期文字和小箭头)
280+ const wrapper = document . createElement ( 'div' ) ;
281+ wrapper . className = 'sidebar-day-toggle' ;
282+
283+ const labelSpan = document . createElement ( 'span' ) ;
284+ labelSpan . className = 'sidebar-day-toggle-label' ;
285+ labelSpan . textContent = rawText ;
286+
287+ const arrowSpan = document . createElement ( 'span' ) ;
288+ arrowSpan . className = 'sidebar-day-toggle-arrow' ;
289+ arrowSpan . textContent = '▾' ;
290+
291+ wrapper . appendChild ( labelSpan ) ;
292+ wrapper . appendChild ( arrowSpan ) ;
293+
294+ // 用 wrapper 替换原始文本节点
295+ li . replaceChild ( wrapper , first ) ;
296+
297+ // 决定默认展开 / 收起:
298+ // - 如果本次是“出现了新的一天”:清空历史,只展开最新一天;
299+ // - 否则若已有用户偏好(state),按偏好来;
300+ // - 否则(首次使用且没有历史):仅“最新一天”展开,其余收起。
301+ let collapsed ;
302+ if ( isNewDay ) {
303+ collapsed = rawText === latestDay ? false : true ;
304+ } else if ( hasAnyState ) {
305+ const saved = state [ rawText ] ;
306+ if ( saved === 'open' ) {
307+ collapsed = false ;
308+ } else if ( saved === 'closed' ) {
309+ collapsed = true ;
310+ } else {
311+ // 新出现的日期:默认跟最新一天策略走
312+ collapsed = rawText === latestDay ? false : true ;
313+ }
314+ } else {
315+ collapsed = rawText === latestDay ? false : true ;
316+ }
317+
318+ if ( collapsed ) {
319+ li . classList . add ( 'sidebar-day-collapsed' ) ;
320+ arrowSpan . textContent = '▸' ;
321+ } else {
322+ li . classList . remove ( 'sidebar-day-collapsed' ) ;
323+ arrowSpan . textContent = '▾' ;
324+ }
325+
326+ wrapper . addEventListener ( 'click' , ( ) => {
327+ const collapsed = li . classList . toggle ( 'sidebar-day-collapsed' ) ;
328+ arrowSpan . textContent = collapsed ? '▸' : '▾' ;
329+ state [ rawText ] = collapsed ? 'closed' : 'open' ;
330+ state . __latestDay = latestDay ;
331+ ensureStateSaved ( ) ;
332+ } ) ;
333+
334+ li . dataset . dayToggleApplied = '1' ;
335+ } ) ;
336+ } ;
337+
338+ // 4. 渲染评论区的 HTML 结构
201339 const renderChatUI = ( ) => {
202340 return `
203341 <div id="paper-chat-container">
@@ -213,7 +351,7 @@ window.$docsify = {
213351 ` ;
214352 } ;
215353
216- // 3 . 获取历史记录 (API)
354+ // 5 . 获取历史记录 (API)
217355 const loadHistory = async ( paperId ) => {
218356 try {
219357 const res = await fetch (
@@ -307,7 +445,7 @@ window.$docsify = {
307445 }
308446 } ;
309447
310- // 4 . 发送消息 (API)
448+ // 6 . 发送消息 (API)
311449 const sendMessage = async ( ) => {
312450 const input = document . getElementById ( 'user-input' ) ;
313451 const btn = document . getElementById ( 'send-btn' ) ;
@@ -527,7 +665,12 @@ window.$docsify = {
527665 }
528666
529667 // ----------------------------------------------------
530- // E. Zotero 元数据注入逻辑 (带延时和唤醒)
668+ // E. 侧边栏按日期折叠
669+ // ----------------------------------------------------
670+ setupCollapsibleSidebarByDay ( ) ;
671+
672+ // ----------------------------------------------------
673+ // F. Zotero 元数据注入逻辑 (带延时和唤醒)
531674 // ----------------------------------------------------
532675 setTimeout ( ( ) => {
533676 try {
0 commit comments