2.6.7
2.6.7 (2026-10-01) — 新增「首行缩进」通用功能(为正文段落首行缩进,自动排除标题/表格/列表/引用/代码块等非正文块;宽度 1~2 个中文字符可调;实时预览与阅读视图双端生效,且段落边界跟随 Obsidian 的「严格换行」设置);首行缩进的作用域收窄到真正的正文区域(修复 Glimpse 提词器等第三方视图被误缩进);首行缩进在阅读视图的逐行缩进改用空 inline-block 占位元素实现(修复后续行比首行多缩进一个空格宽);根治「设置开关改了要重启 Obsidian 才生效」(传播链异常隔离 + 全窗口运行态 + 跨 realm 重绘 + 符号边界提示弹框即时消失四类缺口)
新增功能
- 「首行缩进」(新设置项,默认关闭)—— 正文段落首行自动缩进,非正文块一律排除 — 通用设置区新增开关与宽度滑块(1~2 个中文字符,
1em= 一个中文字符宽)。判定走自研行分类器(纯函数,不依赖宿主语法树),排除标题、表格、无序/有序列表、引用与 callout、代码块、数学块、注释、HTML 块、frontmatter、脚注与链接引用定义、独立块 ID、纯图片段落。仅实时预览与阅读视图生效,源码模式刻意不生效(缩进会把源码本身推右,看起来像误输入的空格)。缩进由 CM6 行装饰驱动(编辑器侧)与样式表(阅读视图侧)共同实现,共用同一份宽度变量。设置项说明里写明它的适用范围与排除项。
问题修复
- 首行缩进的段落边界没有跟随 Obsidian 的「严格换行」设置 — 原先按 CommonMark 语义判定「连续文本行 = 同一段落,只有第一行是首行」,但 Obsidian 的默认设置就是非严格换行(单个换行在渲染中即一次硬换行),此时用户按单回车写段落是「一行即一段」,于是除首行外的每个正文行都漏了缩进。现在读取
app.vault.getConfig('strictLineBreaks')并跟随该设置:非严格(默认)时每个正文行都是段落首行、都缩进;严格时保持原行为(同段续行不缩进)。该设置变化时即时重绘(订阅config-changed并筛该键),段落判定缓存键也带上该标志位。 - 首行缩进影响了 Glimpse 提词器等第三方视图 — 两条作用域都过宽:阅读视图侧按
.markdown-preview-view p选,而markdown-preview-view/markdown-rendered常被第三方视图借去复用主题样式(Glimpse 提词器的内容容器就是glimpse-tp-content markdown-rendered markdown-preview-view),于是提词器里的段落也被缩进;编辑器侧的行装饰则是registerEditorExtension注册的,会挂到工作区里所有 CM6 编辑器上(含第三方视图自建的编辑区)。现在阅读视图规则前缀收一层到.markdown-reading-view(排除项同步加前缀),编辑器侧在build()里要求view.dom.closest('.markdown-source-view')。两侧都要收 —— 编辑器侧是 JS 装饰,只改样式表拦不住。 - 阅读视图里首行缩进的后续行比首行多缩进约 0.3~0.4 个字符宽 — 占位元素原先插在
<br>与紧随的文本节点之间,而 markdown-it 渲染软换行输出的是'<br>\n'(<br>后跟一个换行符,会被 HTML 解析器并进紧随文本节点的开头,DOM 形如"\n乙段内容")。这段前导空白因此不再位于行首、不被「行首空白丢弃」吃掉,而是塌缩成一个空格把该行多推一个空格宽(实测后续行 44.73px vs 首行 40px,差值 4.73px @16px 字号 = 0.3em,正是一个空格)。现在占位元素插到前导空白之后(splitText切开,前导空白留在<br>之后),实测修复后两行左边界均为 40px。 - 阅读视图缺少逐行缩进(单回车换行的后续行不缩进) — 阅读视图里单个回车只是
<p>内的一个<br>,而text-indent只作用于块级容器的首行,<br>之后的文本拿不到缩进(四种纯 CSS 做法实测全部无效,见下)。现在在正文段落里每个<br>之后插入一个空的 inline-block 占位元素把该行撑右:空节点不进文本内容(选中复制不受影响)、height: 0使段落高度与插入前逐像素一致(实测 76.78 = 76.78)、宽度走 CSS 变量(改宽度无需重插)、撤销只需删掉带类名的节点。插入时机靠MutationObserver(childList+subtree,rAF 合并),apply()幂等(只在需要补/摘时动 DOM,避免自激),容器用WeakSet去重。仅在「功能开启 且 非严格换行」时插入(严格换行下<br>只来自显式硬换行、仍属同一段落,不该缩进)。 - 「设置开关改了要重启 Obsidian 才生效」 — 先证明链路本身是通的(静态审计 64 个设置键的消费点 + 机械审计 38 个
Setting块均无遗漏 + CM6 源码核实空事务确实会走到ViewPlugin.update()+ 真实 CM6 端到端实测开关开/关/再关得到 0/3/0 个行装饰),再修掉四类真实缺口:① 传播链可被单点异常静默掐断 ——saveSettings()里syncConfig()→repaintAllEditors()→dirFileCountRefresher.forceRefresh()顺序直连,任一步抛错后面全不执行且无日志,症状与「设置已保存但插件没反应、重启才生效」完全一致;现在逐段 try/catch +console.error。② body 运行态只挂activeDocument—— popout / 悬浮编辑器窗口里的编辑器拿不到 body 开关类与 CSS 变量,新增window-scope.ts的forEachDocument()遍历主窗口 + 各 leaf 的 document(按 document 去重,用node.ownerDocument ?? node判定,因为 popout 的 document 来自另一个 realm、instanceof Document会失败),四个 body 类模块全部改为全窗口应用。③ 重绘用instanceof MarkdownView—— popout 的视图跨 realm,instanceof为假、那些编辑器不会被重绘;改为鸭子判定并给每个编辑器套 try/catch。④ 「符号边界提示」关掉后弹框不消失 —— 它的update()只在选区/文档/几何变化时刷新,而改设置只派发空事务,已显示的弹框要等下次光标移动才消失;补一个「开关翻转」判定并清掉位置缓存。另新增applyRuntimeClasses()自愈点(幂等 + 逐项异常隔离),由saveSettings()、workspace.on('layout-change')(新开 popout 时补挂)、设置面板display()(打开即自愈)三处调用。
界面优化
- 首行缩进的性能与交互 — 段落判定按文档对象缓存(行分类是 O(行数) 的全篇扫描,只在文档真的变化时重算,纯光标移动/滚动复用上次结果);装饰只给可见范围内的段落首行建。
text-indent不破坏 CM6 的光标定位(posAtCoords走布局感知接口,因此点击落点与选区矩形自动跟随)。
完整更新记录:CHANGELOG.md / CHANGELOG.en.md