一条固定在编辑区边缘的标题导轨,风格参照 Codex 的文档导航。它是 gogoga 主题的配套插件,但不依赖主题的任何东西, 单独使用也没问题。
界面语言跟随 Obsidian 的语言设置,支持简体中文和英文。

功能
- 静止时:一列长短一致的短刻度,每个标题对应一条。当前正在阅读的章节对应的 刻度显示为强调色。
- 悬停时:程序坞式的波浪。每条刻度按它到光标距离的高斯衰减变长,所以波峰会 连续地跟随指针移动,而不是在条目之间跳格。
- 预览:波峰所在的章节会显示一张卡片,内容是标题和该章节开头几行(去掉 markdown 标记)。卡片随波峰一起滑动。
- 点击:滚动到对应标题。
- 停留时:指针在导轨上静止片刻,完整大纲就会在旁边展开,按标题层级缩进。 在波浪上来回划动永远不会触发它——只要有真正的移动,计时器就会重新开始,所以只有 真正停下来才会打开面板。
- 面板打开后:导轨变成它的滑块。指针沿刻度往下划,会高亮面板里对应的那一行, 而不是在面板上再叠一张预览卡片;点击即确认并关闭。
面板只是一个普通的浮出层:导轨本身不会移动。手势进行到一半时,光标下面的东西不会 突然挪开;五十个标题的笔记和五个标题的笔记表现完全一样(面板内部滚动,打开时当前 章节就在视野内)。各行从导轨一侧滑入,每行之间有短暂的错开。
离开导轨或面板中的任意一个,都会启动一个 180ms 的关闭计时器,进入另一个则取消它,
这样你才能跨过两者之间的空隙。按 Escape 关闭。展开/收起全局大纲 命令会以固定模式打开
面板——它会一直保持到你关闭为止,并获取键盘焦点,可以用上/下/回车导航。
每个打开的 markdown 面板各有一条导轨;导轨跟随面板拆分,也随面板关闭。
聚焦模式和悬停行强调(gogoga 主题)
「切换聚焦模式」「切换悬停行强调」两个命令用来开关 gogoga 主题自带的写作效果:聚焦模式下只有光标所在的段落保持清晰, 悬停行强调给鼠标所在的行加一层淡底色。可以在 设置 → 快捷键 里绑定快捷键;插件设置页底部也有同样的开关, 开关状态重启后保留。换成别的主题时不起作用。
设置
显示位置(左/右)、导轨出现所需的最少标题数、悬停时是否显示预览卡片、面板的展开方式 (停留展开或仅快捷键)以及停留多久后展开、面板宽度,以及面板展开到第几级标题—— 超过层级上限的标题不会进入面板,但在导轨上仍保留它的刻度。
面板还有几个可选项(默认都关):
- 完整大纲常驻展开:不用悬停,完整大纲一直显示在笔记边上(代替导轨),正文给它让出位置。点标题跳过去,按 Esc 不会关;「全局大纲」命令改成把键盘焦点放进大纲。手机上不生效。
- 点击标题后保持大纲打开:点标题跳过去,面板不收起,按 Esc 或再用一次命令关掉。面板开着时滚动正文,当前章节会自动保持在面板的视野里。
- 只展开当前标题:正在看的(或刚点的)标题全部展开,它的上级一路打开,其余标题折叠到「其他标题显示到第几级」设的层级(默认只显示大标题)。整篇只有一个一级标题时,它当作总标题,层级从它下面一级算起。标题前出现小箭头:单击展开或收起一层,双击整支全部展开;面板获得焦点时也能用 ←/→。
- 显示完整标题:太长的标题换行显示全文。
标题里的 $公式$ 在面板和预览卡片里照常渲染。
关于滚动行为
两种模式都会对 DOM 做虚拟化,因此都要在平滑度上做些让步,换取准确落到目标行。
编辑模式按 cm.viewport(CodeMirror 实际已渲染的范围)分两种情况处理。在这个范围内,
每一行的高度都是实测而非估算的,所以插件自己的计算是精确的,可以平滑滚动。范围之外,
定位交给 EditorView.scrollIntoView,它会反复测量直到目标稳定——位置精确,但只能瞬间
跳转,因为它把滚动推迟到下一次测量阶段,没有可靠的时机去拦截并重新加动画。
朴素的做法有两处错误,而且都很容易被忽略。CM6 报告的块位置是相对文档顶部的,而
scrollTop 是相对滚动容器顶部的,Obsidian 又用 --file-margins 给滚动容器加了内边距——
所以直接使用 lineBlockAt().top 会差出这段内边距(默认 32px),跳转和当前章节追踪都会
受影响。另外,从未渲染过的行只有估算高度,所以根据高度表算出的目标会偏移,偏移量取决于
中间有多少未测量的内容;滚动途中经过这些行时会测量它们,目标还会进一步漂移。
阅读模式是虚拟化的——章节滚出视野后会被整个移出 DOM——所以当目标标题当前已渲染时,
插件会平滑滚动;未渲染时则回退到 Obsidian 基于行号的 applyScroll(瞬间跳转)。
因此近处的标题会平滑滑过去,远处的会直接跳过去。阅读模式下的当前章节追踪使用
getScroll(),它基于行号,不受虚拟化影响。
调参
波浪相关常量位于 main.js 顶部:BASE_W(静止时的刻度长度)、MAX_EXTRA(波峰处增加
的长度)、SIGMA(衰减半径,单位 px),以及对应的高度常量。旁边还有:DWELL_TOL
(多大的漂移仍算作静止)、CLOSE_GRACE、INDENT_STEP,以及行的 STAGGER。
一个 CSS 细节
面板中的行是 <button>,而 Obsidian 核心的按钮规则优先级高于单独的 all: unset。
因此重置规则针对 :hover 和 :focus 又各写了一遍,所有视觉样式都放在多一层选择器的
规则里——否则 :hover 那份 all: unset 会在悬停时把行自身的样式抹掉,行就会显示成
灰色胶囊。