Repository navigation
Releases: YaYII/obsidian-zoomable-reader
Release list
1.3.12
Changelog
1.3.12
- 图里的文字固定 ≤16px,不再被「拉伸」放大。 用户原话:「你的插件和主题在生成 mermaid 里面的
文字打小应该是固定的,不能超过 16px 的大小」。根因是 1.3.9 那条「图表撑满版心」的规则:
它把 svg 连字带线一起放大 —— 一张 478px 的图铺进 900px 版心是 1.9 倍(白板 1280px 时 2.7 倍),
图里 16px 的字渲染成 30~43px。现在图表只保证「不溢出」(max-width):窄图保持原尺寸,
宽图按比例缩进版心;主题侧把图内字号改成固定令牌(节点 14px、边标签与时序消息 12.5px),
不再跟着「设置 → 外观 → 字体大小」和分辨率增益浮动。 - 长标签自动折行,不再「一行顶一个宽度」。 用户原话:「图形里面的文字内容太多了,你应该可以
换行呀,而不是一行顶一个宽度呀」。查证(Mermaid 10.9 实测):普通标签 A[长文本] 从不折行 ——
把 flowchart.wrappingWidth 从 460 改成 120,渲染结果一模一样(同一个 478×42 的框、同一行字)。
也就是说,过去插件里那个「标签最大宽度」滑块对普通标签其实是个空旋钮,这正是抱怨的根源。现在:- 插件把长标签改写成 markdown 字符串(A["
长文本"])—— 只有这种标签 Mermaid 才按折行宽度排版;
同时打开 markdownAutoWrap,默认折行宽度 460 → 260(一行约 16 个汉字,长标签折成 2~3 行); - 白板 / 版面视图在渲染前改写(wrapMermaidFences);阅读视图由 Markdown 后处理器接管:
用 context.getSectionInfo 取回该块源码 → 改写 → 自己重渲染换上去。任何一步失败都保持宿主
原本那张图不换 —— 这块代码最坏的结局必须是「没折行」,而不是「图没了」; - 真实渲染实测(真 Mermaid + 真主题 CSS):同一个 43 字标签,改写前 1 行、框 678px;
改写后 3 行、框 278px,字号不变(14.96px),文字不溢出框。
- 插件把长标签改写成 markdown 字符串(A["
Verification: 单元测试 100 条(新增 tests/mermaid-labels.test.ts 十条:只改长标签、短标签一字不动、
markdown 字符串不重复包装、边标签、幂等、围栏提取);npm run verify:layout 新增 6 条真实渲染断言
(改写前后的行数 / 框宽 / 字号 / 溢出);npm run verify:reading 37 条(「流程图按版心宽度显示」
那条按用户的新要求翻过来,改成断言保持自然宽度 478px)。
1.3.11
- 查看器里给的是「原图」,不是正文里那张缩略图。 用户原话:「查看图片的时候,放大后,点击图片的时候,
你需要确定放大的图片是原图高清的」。正文里的<img>可能带srcset(浏览器按正文的显示尺寸 × DPR
挑一张),也可能被懒加载插件塞了 1px 占位图、真图在data-src里。查看器现在自己挑:
srcset里取分辨率最高的一张,src 是占位图就用data-src。顺带修掉一个真 bug ——
srcset不能按逗号 split:data:URL 里全是逗号,切完每个候选都成了半截地址
(在验证台里的表现是「查看器的图自然尺寸 0」)。 - 右上角多了一个关闭按钮。 用户要求:「放大后的图片,右上角应该有一个关闭的按钮,方便关闭放大的图」。
它钉在图片区域的右上角(跟着安全区走),与图片的缩放平移无关 —— 图拖到哪儿它都在同一个位置;
桌面 34px、手机 44px(Apple HIG 的最小可点尺寸),鼠标 / 触摸 /Esc三条路都能关。
工具条里那个 12px 的小叉已去掉:两个关闭按钮只会让人犹豫。 - 点一下图片 = 落到原图 100%(已经在原图了就收回「适配」)。这是「点击图片 → 确认是原图高清」最直接的
落点:点完读数会写明1600 x 900 px → 1600 x 900 px · 原图 / native。
三条防误触:位移 ≤ 8px、时长 ≤ 500ms、全程单指;拖动平移松手那一下不会触发
(手势层一旦判定为拖动就会 setPointerCapture,pointerup 根本回不到内容元素上)。
双击仍然是放大到 200%,两条路不打架。
Verification: 单元测试 88 条(新增 parseSrcset / pickHiResSource:data URL 里的逗号、
1x/2x 描述符、懒加载占位图、相对地址拼接);npm run verify:gestures 93 条断言,
其中 8 条新断言先在旧代码上跑出红色:懒加载只拿到 1x1、关闭按钮不存在(桌面与手机各两条)、
点图片没反应(0.533 → 0.533)、手机触摸图片同样没反应(0.642),红的时候 85/93。
诚实边界:无头 Chromium 自己给 srcset 挑的就是最大的那张,所以浏览器侧那条「拿到 1600px 原图」
证明的是最终结果;「比浏览器更会挑」这条逻辑由 tests/lightbox.test.ts 的单测钉住。
1.3.10
- 手机上终于看得见那个放大的 icon 了 —— 这一条是用户直接点名的。 触屏设备上按钮以前是
@media (hover: none) { display: none }被整个藏起来的,理由是「轻点图片就能放大,按钮不必占地方」。
用户的回答:「你应该展示放大的 icon,这样我就可以点击了,而不是依靠点击图片,因为图片点击的时候
可能点击到了背景上去了,所以体验不是很好」。现在触屏上按钮常驻可见(不再靠悬停提亮:触屏没有
悬停,0.42 的不透明度等于看不见),视觉 34px,触区用::after外扩到约 46px —— 手指按不准也点得到。
轻点图片这条入口保留,但按钮才是明确的那一个。 - 连点两下按钮不再进编辑。 按钮就贴在图片角上,而「双击」的第二次 tap 正是 Obsidian
「双击进入编辑」的触发点:现在第二次 tap 会被吃掉(第一次照旧打开查看器,且只打开一次)。 - 查看器里的图现在是高清的,不是被压缩后再拉大的。 两个独立原因,都修了:
- 100% 的基准错了:以前「1 个源像素 = 1 个 CSS 像素」,所以在 DPR=3 的手机上,一张 1600px 的
截图被铺到 4800 个物理像素上 —— 横向拉伸 3 倍,这就是用户说的「放大查看都模糊了」。现在按
naturalWidth / devicePixelRatio排版:100% 就是原图分辨率(1 源像素 = 1 设备像素),读数也
对得上(1600 x 900 px → 1600 x 900 px)。再往上放大才是放大镜。 - 画布上带着
will-change: transform:它把画面钉死在提层时的比例上,之后放大只是把旧位图拉大
(照片发软、图表里的 CSS 文字直接糊)。已去掉。 - 另外,打开小图不再「适配」放大(适配比例封顶 100%),并解除主题/宿主塞给 img 的
max-width: 100%(它会把刚摆好的 1:1 又压回去,窄屏上高清立刻失效)。
- 100% 的基准错了:以前「1 个源像素 = 1 个 CSS 像素」,所以在 DPR=3 的手机上,一张 1600px 的
- 细节:插件的手机手势拦截器现在会避开自己的放大按钮 —— 按钮里的图标是
<svg>,不排除就会被
「这是不是图表」的判定当成一张 18px 的图表打开。
Verification: 单元测试 82 条(新增 tests/lightbox.test.ts 钉住 1:1 的换算,含 devicePixelRatio
不老实的情况);npm run verify:gestures 82 条断言,npm run verify:reading 36 条断言。
新断言先在旧代码上跑过一遍确认它们能失败:1.3.9 上触屏的按钮是 display=none opacity=0.42 0x0、
小图打开是 426%、will-change 计算值是 transform。
1.3.9
- Fixed: flow charts (and every other diagram) ignored the line width. 1.3.7's width rules only
targetedimgandvideo, but a Mermaid flow chart is an<svg>— and themes set
flowchart.useMaxWidth: false(that is what makes boxes follow the text), so Mermaid renders at
its natural width and the diagram stayed at 478 px inside a 900 px column, or overflowed on a
phone. The rules now cover.mermaid svg,.block-language-mermaid svg,
.excalidraw-svg svgand.block-language-chart svg/canvasas well, and the plugin's own
view got the same treatment instyles.css. - Scaling is by ratio (SVG is vector, so the text scales with it): measured in a real browser, a
478 × 98 diagram becomes 900 × 184 with the 478:98 ratio intact, and on a 390 px phone viewport
both the diagram and a photo land at 342 px — one screen wide, as asked. Double-tap still opens the
zoomable viewer, which is where detail reading now happens. - The setting is now called 阅读视图图片/图表宽度 (it always governed both, the label just lied).
Verification: npm run verify:reading is now 32 assertions — the new ones measure the diagram in
the DOM: fill → 900 px with the aspect ratio preserved, contain → stays 478 px, and on a phone
viewport the diagram and the photo are both clamped to the same screen width.
1.3.8
- Fixed: double tapping still opened the editor. 1.3.7 only intercepted double taps on images,
but Obsidian's double-tap-to-edit is a gesture of the whole reading view — tapping text
triggers it too, so in practice nothing changed. The interception now covers every double tap
inside the reading view (text, images, diagrams); double tapping an image or diagram opens the
zoomable viewer instead. Two separate switches: 手机:双击不进编辑 and 手机:双击图片 = 放大查看. - Three more real bugs, all found by the browser harness rather than by reading the code:
- The same event was handled twice (the module listens on both
windowanddocumentin
the capture phase), so one tap looked like two and a single tap opened the viewer. Events are now
deduplicated. - A two-finger pinch was counted as a tap, poisoning the next double tap. Multi-touch now
cancels tap tracking, and onlytouchendcounts as a tap (pointerup/clickare used for
swallowing only). - Within one gesture,
touchstarttargets the image buttouchendand the synthetic
dblclicktarget the container, so the hit test concluded "text" and the image never zoomed.
The hit is now resolved from the touch-start element and the coordinates as a fallback.
- The same event was handled twice (the module listens on both
- A touch double tap also produces a synthetic
dblclick(in an unstable order) and both used to
open the viewer; they now share a short window so one double tap opens exactly once. - 手机:双击自检 (off by default) shows a notice for every intercepted double tap — if a gesture
still misbehaves on a real phone, the notice tells whether the plugin saw it at all.
Verification: npm run verify:reading — 28 assertions. Besides the earlier reading-view
checks: a single tap does nothing, a double tap on text does not enter editing and opens nothing,
a double tap on an image opens the viewer exactly once and the simulated editor handler records
zero events, gestures outside the reading view are ignored, and mobile: false (desktop) does
not intercept at all.
1.3.7
- Images follow the line width, scaled by ratio. In the reading view every image is now scaled to
exactly the line width you chose (900 px by default) with `height: auto`, so nothing is cropped
or stretched and a 600 × 200 screenshot becomes 900 × 300. Explicit widths (`![[image.png|300]]`)
are normalised too — one page, one image width, as asked. Two other policies stay available
(fit within the line width, natural size), and the rules only ever touch
`.markdown-reading-view`, never the editor. - Mobile: double-tapping an image zooms it instead of entering editing. In Obsidian's reading
view a double tap on an image means "edit", which is exactly wrong while reading on a phone. The
plugin now intercepts that gesture inside the reading view and opens the zoomable viewer instead;
the edit path is suppressed (use the menu button when you really want to edit). A single tap does
nothing new, gestures outside the reading view are ignored, and desktop is untouched — as
asked, this is a mobile behaviour. - Two ordering bugs found by the browser harness, both fixed rather than papered over. A touch
double tap also produces a synthetic `dblclick`, and which one arrives first is not stable — the
first version opened the viewer twice. Both paths now share a short mutual-exclusion window: one
double tap opens the viewer exactly once. The harness also had to be honest: its simulated
"Obsidian edit" listener originally counted every tap, which proved nothing; it now does real
double-tap detection, so "0 edits" means the double tap was genuinely consumed.
Verification: npm run verify:reading is now 26 assertions — the image policies are measured
in the DOM (fill: 600 px image → 900 px, ratio still 3:1; contain: stays 600 px; natural: no rules
injected), the desktop path is asserted not to open the viewer, and on a phone viewport a real CDP
double tap opens the viewer exactly once while the simulated edit handler records zero events.
1.3.6
- The reading view itself now has a width and a zoom. Obsidian's own reading view is not taken
over — the plugin changes two CSS properties of it: the line width (default 900 px, the value
that was asked for, with 760–1200 px and 跟随主题 / follow-the-theme options) and a zoom level
(60 %–200 %, default 100 %). Your theme files are untouched, and follow the theme + 100 % injects
nothing beyond one `touch-action` line. - Zoom reflows instead of stretching. It is applied as CSS `zoom` with the sizer's `max-width`
divided by the same factor, so the text gets bigger while the measure on screen stays exactly the
line width you picked. A screenshot-level stretch would have changed the line length; this does
not. - **You operate it: pinch, ...
1.3.11
Changelog
1.3.11
- 查看器里给的是「原图」,不是正文里那张缩略图。 用户原话:「查看图片的时候,放大后,点击图片的时候,
你需要确定放大的图片是原图高清的」。正文里的<img>可能带srcset(浏览器按正文的显示尺寸 × DPR
挑一张),也可能被懒加载插件塞了 1px 占位图、真图在data-src里。查看器现在自己挑:
srcset里取分辨率最高的一张,src 是占位图就用data-src。顺带修掉一个真 bug ——
srcset不能按逗号 split:data:URL 里全是逗号,切完每个候选都成了半截地址
(在验证台里的表现是「查看器的图自然尺寸 0」)。 - 右上角多了一个关闭按钮。 用户要求:「放大后的图片,右上角应该有一个关闭的按钮,方便关闭放大的图」。
它钉在图片区域的右上角(跟着安全区走),与图片的缩放平移无关 —— 图拖到哪儿它都在同一个位置;
桌面 34px、手机 44px(Apple HIG 的最小可点尺寸),鼠标 / 触摸 /Esc三条路都能关。
工具条里那个 12px 的小叉已去掉:两个关闭按钮只会让人犹豫。 - 点一下图片 = 落到原图 100%(已经在原图了就收回「适配」)。这是「点击图片 → 确认是原图高清」最直接的
落点:点完读数会写明1600 x 900 px → 1600 x 900 px · 原图 / native。
三条防误触:位移 ≤ 8px、时长 ≤ 500ms、全程单指;拖动平移松手那一下不会触发
(手势层一旦判定为拖动就会 setPointerCapture,pointerup 根本回不到内容元素上)。
双击仍然是放大到 200%,两条路不打架。
Verification: 单元测试 88 条(新增 parseSrcset / pickHiResSource:data URL 里的逗号、
1x/2x 描述符、懒加载占位图、相对地址拼接);npm run verify:gestures 93 条断言,
其中 8 条新断言先在旧代码上跑出红色:懒加载只拿到 1x1、关闭按钮不存在(桌面与手机各两条)、
点图片没反应(0.533 → 0.533)、手机触摸图片同样没反应(0.642),红的时候 85/93。
诚实边界:无头 Chromium 自己给 srcset 挑的就是最大的那张,所以浏览器侧那条「拿到 1600px 原图」
证明的是最终结果;「比浏览器更会挑」这条逻辑由 tests/lightbox.test.ts 的单测钉住。
1.3.10
- 手机上终于看得见那个放大的 icon 了 —— 这一条是用户直接点名的。 触屏设备上按钮以前是
@media (hover: none) { display: none }被整个藏起来的,理由是「轻点图片就能放大,按钮不必占地方」。
用户的回答:「你应该展示放大的 icon,这样我就可以点击了,而不是依靠点击图片,因为图片点击的时候
可能点击到了背景上去了,所以体验不是很好」。现在触屏上按钮常驻可见(不再靠悬停提亮:触屏没有
悬停,0.42 的不透明度等于看不见),视觉 34px,触区用::after外扩到约 46px —— 手指按不准也点得到。
轻点图片这条入口保留,但按钮才是明确的那一个。 - 连点两下按钮不再进编辑。 按钮就贴在图片角上,而「双击」的第二次 tap 正是 Obsidian
「双击进入编辑」的触发点:现在第二次 tap 会被吃掉(第一次照旧打开查看器,且只打开一次)。 - 查看器里的图现在是高清的,不是被压缩后再拉大的。 两个独立原因,都修了:
- 100% 的基准错了:以前「1 个源像素 = 1 个 CSS 像素」,所以在 DPR=3 的手机上,一张 1600px 的
截图被铺到 4800 个物理像素上 —— 横向拉伸 3 倍,这就是用户说的「放大查看都模糊了」。现在按
naturalWidth / devicePixelRatio排版:100% 就是原图分辨率(1 源像素 = 1 设备像素),读数也
对得上(1600 x 900 px → 1600 x 900 px)。再往上放大才是放大镜。 - 画布上带着
will-change: transform:它把画面钉死在提层时的比例上,之后放大只是把旧位图拉大
(照片发软、图表里的 CSS 文字直接糊)。已去掉。 - 另外,打开小图不再「适配」放大(适配比例封顶 100%),并解除主题/宿主塞给 img 的
max-width: 100%(它会把刚摆好的 1:1 又压回去,窄屏上高清立刻失效)。
- 100% 的基准错了:以前「1 个源像素 = 1 个 CSS 像素」,所以在 DPR=3 的手机上,一张 1600px 的
- 细节:插件的手机手势拦截器现在会避开自己的放大按钮 —— 按钮里的图标是
<svg>,不排除就会被
「这是不是图表」的判定当成一张 18px 的图表打开。
Verification: 单元测试 82 条(新增 tests/lightbox.test.ts 钉住 1:1 的换算,含 devicePixelRatio
不老实的情况);npm run verify:gestures 82 条断言,npm run verify:reading 36 条断言。
新断言先在旧代码上跑过一遍确认它们能失败:1.3.9 上触屏的按钮是 display=none opacity=0.42 0x0、
小图打开是 426%、will-change 计算值是 transform。
1.3.9
- Fixed: flow charts (and every other diagram) ignored the line width. 1.3.7's width rules only
targetedimgandvideo, but a Mermaid flow chart is an<svg>— and themes set
flowchart.useMaxWidth: false(that is what makes boxes follow the text), so Mermaid renders at
its natural width and the diagram stayed at 478 px inside a 900 px column, or overflowed on a
phone. The rules now cover.mermaid svg,.block-language-mermaid svg,
.excalidraw-svg svgand.block-language-chart svg/canvasas well, and the plugin's own
view got the same treatment instyles.css. - Scaling is by ratio (SVG is vector, so the text scales with it): measured in a real browser, a
478 × 98 diagram becomes 900 × 184 with the 478:98 ratio intact, and on a 390 px phone viewport
both the diagram and a photo land at 342 px — one screen wide, as asked. Double-tap still opens the
zoomable viewer, which is where detail reading now happens. - The setting is now called 阅读视图图片/图表宽度 (it always governed both, the label just lied).
Verification: npm run verify:reading is now 32 assertions — the new ones measure the diagram in
the DOM: fill → 900 px with the aspect ratio preserved, contain → stays 478 px, and on a phone
viewport the diagram and the photo are both clamped to the same screen width.
1.3.8
- Fixed: double tapping still opened the editor. 1.3.7 only intercepted double taps on images,
but Obsidian's double-tap-to-edit is a gesture of the whole reading view — tapping text
triggers it too, so in practice nothing changed. The interception now covers every double tap
inside the reading view (text, images, diagrams); double tapping an image or diagram opens the
zoomable viewer instead. Two separate switches: 手机:双击不进编辑 and 手机:双击图片 = 放大查看. - Three more real bugs, all found by the browser harness rather than by reading the code:
- The same event was handled twice (the module listens on both
windowanddocumentin
the capture phase), so one tap looked like two and a single tap opened the viewer. Events are now
deduplicated. - A two-finger pinch was counted as a tap, poisoning the next double tap. Multi-touch now
cancels tap tracking, and onlytouchendcounts as a tap (pointerup/clickare used for
swallowing only). - Within one gesture,
touchstarttargets the image buttouchendand the synthetic
dblclicktarget the container, so the hit test concluded "text" and the image never zoomed.
The hit is now resolved from the touch-start element and the coordinates as a fallback.
- The same event was handled twice (the module listens on both
- A touch double tap also produces a synthetic
dblclick(in an unstable order) and both used to
open the viewer; they now share a short window so one double tap opens exactly once. - 手机:双击自检 (off by default) shows a notice for every intercepted double tap — if a gesture
still misbehaves on a real phone, the notice tells whether the plugin saw it at all.
Verification: npm run verify:reading — 28 assertions. Besides the earlier reading-view
checks: a single tap does nothing, a double tap on text does not enter editing and opens nothing,
a double tap on an image opens the viewer exactly once and the simulated editor handler records
zero events, gestures outside the reading view are ignored, and mobile: false (desktop) does
not intercept at all.
1.3.7
- Images follow the line width, scaled by ratio. In the reading view every image is now scaled to
exactly the line width you chose (900 px by default) with `height: auto`, so nothing is cropped
or stretched and a 600 × 200 screenshot becomes 900 × 300. Explicit widths (`![[image.png|300]]`)
are normalised too — one page, one image width, as asked. Two other policies stay available
(fit within the line width, natural size), and the rules only ever touch
`.markdown-reading-view`, never the editor. - Mobile: double-tapping an image zooms it instead of entering editing. In Obsidian's reading
view a double tap on an image means "edit", which is exactly wrong while reading on a phone. The
plugin now intercepts that gesture inside the reading view and opens the zoomable viewer instead;
the edit path is suppressed (use the menu button when you really want to edit). A single tap does
nothing new, gestures outside the reading view are ignored, and desktop is untouched — as
asked, this is a mobile behaviour. - Two ordering bugs found by the browser harness, both fixed rather than papered over. A touch
double tap also produces a synthetic `dblclick`, and which one arrives first is not stable — the
first version opened the viewer twice. Both paths now share a short mutual-exclusion window: one
double tap opens the viewer exactly once. The harness also had to be honest: its simulated
"Obsidian edit" listener originally counted every tap, which proved nothing; it now does real
double-tap detection, so "0 edits" means the double tap was genuinely consumed.
Verification: npm run verify:reading is now 26 assertions — the image policies are measured
in the DOM (fill: 600 px image → 900 px, ratio still 3:1; contain: stays 600 px; natural: no rules
injected), the desktop path is asserted not to open the viewer, and on a phone viewport a real CDP
double tap opens the viewer exactly once while the simulated edit handler records zero events.
1.3.6
- The reading view itself now has a width and a zoom. Obsidian's own reading view is not taken
over — the plugin changes two CSS properties of it: the line width (default 900 px, the value
that was asked for, with 760–1200 px and 跟随主题 / follow-the-theme options) and a zoom level
(60 %–200 %, default 100 %). Your theme files are untouched, and follow the theme + 100 % injects
nothing beyond one `touch-action` line. - Zoom reflows instead of stretching. It is applied as CSS `zoom` with the sizer's `max-width`
divided by the same factor, so the text gets bigger while the measure on screen stays exactly the
line width you picked. A screenshot-level stretch would have changed the line length; this does
not. - You operate it: pinch, Ctrl+wheel, or commands. Pinch inside the reading view on mobile,
Ctrl / ⌘ + wheel on desktop, or the three new commands (阅读视图放大 / 缩小 / 复位到 100 %,
each showing the new level). Only two-finger pinches and Ctrl+wheel are intercepted — one-finger
scrolling, taps and the plain wheel remain Obsidian's. - A real bug found by the browser harness. The first implementation listened for pointer events
and the browser claimed the two-finger gesture for itself: of ten gesture frames only two arrived
(pointercancel). The fix is to listen for `touchstart`/`touchmove` and to declare
`touch-action: pan-y` on the reading view — vertical one-finger scrolling still belongs to
Obsidian, while the two-finger pinch now belongs to the plugin (measured: zoom 1 → 1.86 in one
pinch). - New settings group 阅读视图(Obsidian 自带的页面)/ Reading view, with the three settings above.
Verification: npm run verify:reading — 17 assertions in a real Chromium against a page that
mimics the reading view (a theme width of 720 px, a long document): follow-the-theme really is
720 px with no width rules injected, 900 px really measures 900 px, zoom 150 % keeps the on-screen
measure at 900 px while paragraphs get 1.5x taller, the clamp holds at 60–200...
1.3.10
Changelog
1.3.10
- 手机上终于看得见那个放大的 icon 了 —— 这一条是用户直接点名的。 触屏设备上按钮以前是
@media (hover: none) { display: none }被整个藏起来的,理由是「轻点图片就能放大,按钮不必占地方」。
用户的回答:「你应该展示放大的 icon,这样我就可以点击了,而不是依靠点击图片,因为图片点击的时候
可能点击到了背景上去了,所以体验不是很好」。现在触屏上按钮常驻可见(不再靠悬停提亮:触屏没有
悬停,0.42 的不透明度等于看不见),视觉 34px,触区用::after外扩到约 46px —— 手指按不准也点得到。
轻点图片这条入口保留,但按钮才是明确的那一个。 - 连点两下按钮不再进编辑。 按钮就贴在图片角上,而「双击」的第二次 tap 正是 Obsidian
「双击进入编辑」的触发点:现在第二次 tap 会被吃掉(第一次照旧打开查看器,且只打开一次)。 - 查看器里的图现在是高清的,不是被压缩后再拉大的。 两个独立原因,都修了:
- 100% 的基准错了:以前「1 个源像素 = 1 个 CSS 像素」,所以在 DPR=3 的手机上,一张 1600px 的
截图被铺到 4800 个物理像素上 —— 横向拉伸 3 倍,这就是用户说的「放大查看都模糊了」。现在按
naturalWidth / devicePixelRatio排版:100% 就是原图分辨率(1 源像素 = 1 设备像素),读数也
对得上(1600 x 900 px → 1600 x 900 px)。再往上放大才是放大镜。 - 画布上带着
will-change: transform:它把画面钉死在提层时的比例上,之后放大只是把旧位图拉大
(照片发软、图表里的 CSS 文字直接糊)。已去掉。 - 另外,打开小图不再「适配」放大(适配比例封顶 100%),并解除主题/宿主塞给 img 的
max-width: 100%(它会把刚摆好的 1:1 又压回去,窄屏上高清立刻失效)。
- 100% 的基准错了:以前「1 个源像素 = 1 个 CSS 像素」,所以在 DPR=3 的手机上,一张 1600px 的
- 细节:插件的手机手势拦截器现在会避开自己的放大按钮 —— 按钮里的图标是
<svg>,不排除就会被
「这是不是图表」的判定当成一张 18px 的图表打开。
Verification: 单元测试 82 条(新增 tests/lightbox.test.ts 钉住 1:1 的换算,含 devicePixelRatio
不老实的情况);npm run verify:gestures 82 条断言,npm run verify:reading 36 条断言。
新断言先在旧代码上跑过一遍确认它们能失败:1.3.9 上触屏的按钮是 display=none opacity=0.42 0x0、
小图打开是 426%、will-change 计算值是 transform。
1.3.9
- Fixed: flow charts (and every other diagram) ignored the line width. 1.3.7's width rules only
targetedimgandvideo, but a Mermaid flow chart is an<svg>— and themes set
flowchart.useMaxWidth: false(that is what makes boxes follow the text), so Mermaid renders at
its natural width and the diagram stayed at 478 px inside a 900 px column, or overflowed on a
phone. The rules now cover.mermaid svg,.block-language-mermaid svg,
.excalidraw-svg svgand.block-language-chart svg/canvasas well, and the plugin's own
view got the same treatment instyles.css. - Scaling is by ratio (SVG is vector, so the text scales with it): measured in a real browser, a
478 × 98 diagram becomes 900 × 184 with the 478:98 ratio intact, and on a 390 px phone viewport
both the diagram and a photo land at 342 px — one screen wide, as asked. Double-tap still opens the
zoomable viewer, which is where detail reading now happens. - The setting is now called 阅读视图图片/图表宽度 (it always governed both, the label just lied).
Verification: npm run verify:reading is now 32 assertions — the new ones measure the diagram in
the DOM: fill → 900 px with the aspect ratio preserved, contain → stays 478 px, and on a phone
viewport the diagram and the photo are both clamped to the same screen width.
1.3.8
- Fixed: double tapping still opened the editor. 1.3.7 only intercepted double taps on images,
but Obsidian's double-tap-to-edit is a gesture of the whole reading view — tapping text
triggers it too, so in practice nothing changed. The interception now covers every double tap
inside the reading view (text, images, diagrams); double tapping an image or diagram opens the
zoomable viewer instead. Two separate switches: 手机:双击不进编辑 and 手机:双击图片 = 放大查看. - Three more real bugs, all found by the browser harness rather than by reading the code:
- The same event was handled twice (the module listens on both
windowanddocumentin
the capture phase), so one tap looked like two and a single tap opened the viewer. Events are now
deduplicated. - A two-finger pinch was counted as a tap, poisoning the next double tap. Multi-touch now
cancels tap tracking, and onlytouchendcounts as a tap (pointerup/clickare used for
swallowing only). - Within one gesture,
touchstarttargets the image buttouchendand the synthetic
dblclicktarget the container, so the hit test concluded "text" and the image never zoomed.
The hit is now resolved from the touch-start element and the coordinates as a fallback.
- The same event was handled twice (the module listens on both
- A touch double tap also produces a synthetic
dblclick(in an unstable order) and both used to
open the viewer; they now share a short window so one double tap opens exactly once. - 手机:双击自检 (off by default) shows a notice for every intercepted double tap — if a gesture
still misbehaves on a real phone, the notice tells whether the plugin saw it at all.
Verification: npm run verify:reading — 28 assertions. Besides the earlier reading-view
checks: a single tap does nothing, a double tap on text does not enter editing and opens nothing,
a double tap on an image opens the viewer exactly once and the simulated editor handler records
zero events, gestures outside the reading view are ignored, and mobile: false (desktop) does
not intercept at all.
1.3.7
- Images follow the line width, scaled by ratio. In the reading view every image is now scaled to
exactly the line width you chose (900 px by default) with `height: auto`, so nothing is cropped
or stretched and a 600 × 200 screenshot becomes 900 × 300. Explicit widths (`![[image.png|300]]`)
are normalised too — one page, one image width, as asked. Two other policies stay available
(fit within the line width, natural size), and the rules only ever touch
`.markdown-reading-view`, never the editor. - Mobile: double-tapping an image zooms it instead of entering editing. In Obsidian's reading
view a double tap on an image means "edit", which is exactly wrong while reading on a phone. The
plugin now intercepts that gesture inside the reading view and opens the zoomable viewer instead;
the edit path is suppressed (use the menu button when you really want to edit). A single tap does
nothing new, gestures outside the reading view are ignored, and desktop is untouched — as
asked, this is a mobile behaviour. - Two ordering bugs found by the browser harness, both fixed rather than papered over. A touch
double tap also produces a synthetic `dblclick`, and which one arrives first is not stable — the
first version opened the viewer twice. Both paths now share a short mutual-exclusion window: one
double tap opens the viewer exactly once. The harness also had to be honest: its simulated
"Obsidian edit" listener originally counted every tap, which proved nothing; it now does real
double-tap detection, so "0 edits" means the double tap was genuinely consumed.
Verification: npm run verify:reading is now 26 assertions — the image policies are measured
in the DOM (fill: 600 px image → 900 px, ratio still 3:1; contain: stays 600 px; natural: no rules
injected), the desktop path is asserted not to open the viewer, and on a phone viewport a real CDP
double tap opens the viewer exactly once while the simulated edit handler records zero events.
1.3.6
- The reading view itself now has a width and a zoom. Obsidian's own reading view is not taken
over — the plugin changes two CSS properties of it: the line width (default 900 px, the value
that was asked for, with 760–1200 px and 跟随主题 / follow-the-theme options) and a zoom level
(60 %–200 %, default 100 %). Your theme files are untouched, and follow the theme + 100 % injects
nothing beyond one `touch-action` line. - Zoom reflows instead of stretching. It is applied as CSS `zoom` with the sizer's `max-width`
divided by the same factor, so the text gets bigger while the measure on screen stays exactly the
line width you picked. A screenshot-level stretch would have changed the line length; this does
not. - You operate it: pinch, Ctrl+wheel, or commands. Pinch inside the reading view on mobile,
Ctrl / ⌘ + wheel on desktop, or the three new commands (阅读视图放大 / 缩小 / 复位到 100 %,
each showing the new level). Only two-finger pinches and Ctrl+wheel are intercepted — one-finger
scrolling, taps and the plain wheel remain Obsidian's. - A real bug found by the browser harness. The first implementation listened for pointer events
and the browser claimed the two-finger gesture for itself: of ten gesture frames only two arrived
(pointercancel). The fix is to listen for `touchstart`/`touchmove` and to declare
`touch-action: pan-y` on the reading view — vertical one-finger scrolling still belongs to
Obsidian, while the two-finger pinch now belongs to the plugin (measured: zoom 1 → 1.86 in one
pinch). - New settings group 阅读视图(Obsidian 自带的页面)/ Reading view, with the three settings above.
Verification: npm run verify:reading — 17 assertions in a real Chromium against a page that
mimics the reading view (a theme width of 720 px, a long document): follow-the-theme really is
720 px with no width rules injected, 900 px really measures 900 px, zoom 150 % keeps the on-screen
measure at 900 px while paragraphs get 1.5x taller, the clamp holds at 60–200 %, Ctrl+wheel zooms
inside the reading view and is ignored outside it (and after the listener is removed), and on a phone
viewport a real CDP two-finger pinch raises the zoom while the throttling keeps the reflow count
below the frame count.
1.3.5
- The card concept is gone. Whiteboard mode used to compile the note into a board of cards:
one card per heading, connectors, folding, a card limit, and a column or flow layout. It is now
what it should have been: one page, 1280 px wide, on a surface you pan, zoom and scroll. The
width setting belongs to the page, not to a card. - Removed along with it:
src/board-model.ts(the heading→card compiler),src/board-layout.ts
(tidy tree / single-column geometry),src/board-render.ts(card DOM, connectors, focus
buttons), the five card settings (排版 / 展开层级 / 间距 / 连线 / 数量上限), every card style in
styles.css, and the 43 unit tests that covered them. The verification suite now asserts the
absence: no card class names or card settings in the built bundle, no card modules in the tree,
and zero card artifacts in the live DOM. - What is left is the part that ma...
1.3.9
Changelog
1.3.9
- Fixed: flow charts (and every other diagram) ignored the line width. 1.3.7's width rules only
targetedimgandvideo, but a Mermaid flow chart is an<svg>— and themes set
flowchart.useMaxWidth: false(that is what makes boxes follow the text), so Mermaid renders at
its natural width and the diagram stayed at 478 px inside a 900 px column, or overflowed on a
phone. The rules now cover.mermaid svg,.block-language-mermaid svg,
.excalidraw-svg svgand.block-language-chart svg/canvasas well, and the plugin's own
view got the same treatment instyles.css. - Scaling is by ratio (SVG is vector, so the text scales with it): measured in a real browser, a
478 × 98 diagram becomes 900 × 184 with the 478:98 ratio intact, and on a 390 px phone viewport
both the diagram and a photo land at 342 px — one screen wide, as asked. Double-tap still opens the
zoomable viewer, which is where detail reading now happens. - The setting is now called 阅读视图图片/图表宽度 (it always governed both, the label just lied).
Verification: npm run verify:reading is now 32 assertions — the new ones measure the diagram in
the DOM: fill → 900 px with the aspect ratio preserved, contain → stays 478 px, and on a phone
viewport the diagram and the photo are both clamped to the same screen width.
1.3.8
- Fixed: double tapping still opened the editor. 1.3.7 only intercepted double taps on images,
but Obsidian's double-tap-to-edit is a gesture of the whole reading view — tapping text
triggers it too, so in practice nothing changed. The interception now covers every double tap
inside the reading view (text, images, diagrams); double tapping an image or diagram opens the
zoomable viewer instead. Two separate switches: 手机:双击不进编辑 and 手机:双击图片 = 放大查看. - Three more real bugs, all found by the browser harness rather than by reading the code:
- The same event was handled twice (the module listens on both
windowanddocumentin
the capture phase), so one tap looked like two and a single tap opened the viewer. Events are now
deduplicated. - A two-finger pinch was counted as a tap, poisoning the next double tap. Multi-touch now
cancels tap tracking, and onlytouchendcounts as a tap (pointerup/clickare used for
swallowing only). - Within one gesture,
touchstarttargets the image buttouchendand the synthetic
dblclicktarget the container, so the hit test concluded "text" and the image never zoomed.
The hit is now resolved from the touch-start element and the coordinates as a fallback.
- The same event was handled twice (the module listens on both
- A touch double tap also produces a synthetic
dblclick(in an unstable order) and both used to
open the viewer; they now share a short window so one double tap opens exactly once. - 手机:双击自检 (off by default) shows a notice for every intercepted double tap — if a gesture
still misbehaves on a real phone, the notice tells whether the plugin saw it at all.
Verification: npm run verify:reading — 28 assertions. Besides the earlier reading-view
checks: a single tap does nothing, a double tap on text does not enter editing and opens nothing,
a double tap on an image opens the viewer exactly once and the simulated editor handler records
zero events, gestures outside the reading view are ignored, and mobile: false (desktop) does
not intercept at all.
1.3.7
- Images follow the line width, scaled by ratio. In the reading view every image is now scaled to
exactly the line width you chose (900 px by default) with `height: auto`, so nothing is cropped
or stretched and a 600 × 200 screenshot becomes 900 × 300. Explicit widths (`![[image.png|300]]`)
are normalised too — one page, one image width, as asked. Two other policies stay available
(fit within the line width, natural size), and the rules only ever touch
`.markdown-reading-view`, never the editor. - Mobile: double-tapping an image zooms it instead of entering editing. In Obsidian's reading
view a double tap on an image means "edit", which is exactly wrong while reading on a phone. The
plugin now intercepts that gesture inside the reading view and opens the zoomable viewer instead;
the edit path is suppressed (use the menu button when you really want to edit). A single tap does
nothing new, gestures outside the reading view are ignored, and desktop is untouched — as
asked, this is a mobile behaviour. - Two ordering bugs found by the browser harness, both fixed rather than papered over. A touch
double tap also produces a synthetic `dblclick`, and which one arrives first is not stable — the
first version opened the viewer twice. Both paths now share a short mutual-exclusion window: one
double tap opens the viewer exactly once. The harness also had to be honest: its simulated
"Obsidian edit" listener originally counted every tap, which proved nothing; it now does real
double-tap detection, so "0 edits" means the double tap was genuinely consumed.
Verification: npm run verify:reading is now 26 assertions — the image policies are measured
in the DOM (fill: 600 px image → 900 px, ratio still 3:1; contain: stays 600 px; natural: no rules
injected), the desktop path is asserted not to open the viewer, and on a phone viewport a real CDP
double tap opens the viewer exactly once while the simulated edit handler records zero events.
1.3.6
- The reading view itself now has a width and a zoom. Obsidian's own reading view is not taken
over — the plugin changes two CSS properties of it: the line width (default 900 px, the value
that was asked for, with 760–1200 px and 跟随主题 / follow-the-theme options) and a zoom level
(60 %–200 %, default 100 %). Your theme files are untouched, and follow the theme + 100 % injects
nothing beyond one `touch-action` line. - Zoom reflows instead of stretching. It is applied as CSS `zoom` with the sizer's `max-width`
divided by the same factor, so the text gets bigger while the measure on screen stays exactly the
line width you picked. A screenshot-level stretch would have changed the line length; this does
not. - You operate it: pinch, Ctrl+wheel, or commands. Pinch inside the reading view on mobile,
Ctrl / ⌘ + wheel on desktop, or the three new commands (阅读视图放大 / 缩小 / 复位到 100 %,
each showing the new level). Only two-finger pinches and Ctrl+wheel are intercepted — one-finger
scrolling, taps and the plain wheel remain Obsidian's. - A real bug found by the browser harness. The first implementation listened for pointer events
and the browser claimed the two-finger gesture for itself: of ten gesture frames only two arrived
(pointercancel). The fix is to listen for `touchstart`/`touchmove` and to declare
`touch-action: pan-y` on the reading view — vertical one-finger scrolling still belongs to
Obsidian, while the two-finger pinch now belongs to the plugin (measured: zoom 1 → 1.86 in one
pinch). - New settings group 阅读视图(Obsidian 自带的页面)/ Reading view, with the three settings above.
Verification: npm run verify:reading — 17 assertions in a real Chromium against a page that
mimics the reading view (a theme width of 720 px, a long document): follow-the-theme really is
720 px with no width rules injected, 900 px really measures 900 px, zoom 150 % keeps the on-screen
measure at 900 px while paragraphs get 1.5x taller, the clamp holds at 60–200 %, Ctrl+wheel zooms
inside the reading view and is ignored outside it (and after the listener is removed), and on a phone
viewport a real CDP two-finger pinch raises the zoom while the throttling keeps the reflow count
below the frame count.
1.3.5
- The card concept is gone. Whiteboard mode used to compile the note into a board of cards:
one card per heading, connectors, folding, a card limit, and a column or flow layout. It is now
what it should have been: one page, 1280 px wide, on a surface you pan, zoom and scroll. The
width setting belongs to the page, not to a card. - Removed along with it:
src/board-model.ts(the heading→card compiler),src/board-layout.ts
(tidy tree / single-column geometry),src/board-render.ts(card DOM, connectors, focus
buttons), the five card settings (排版 / 展开层级 / 间距 / 连线 / 数量上限), every card style in
styles.css, and the 43 unit tests that covered them. The verification suite now asserts the
absence: no card class names or card settings in the built bundle, no card modules in the tree,
and zero card artifacts in the live DOM. - What is left is the part that made the plugin worth having: one page whose width is a setting
(1280 px default, A5/A4 presets one slider step away), recompiled every time you enter the mode and
following the note as you edit, with the same gesture layer as before — drag to pan, pinch to zoom,
适配宽度 to pull the whole page into the viewport and then simply scroll down. - Settings are now: 打开笔记时的默认模式 / default mode and 白板版面宽度 / whiteboard page width.
Verification: npm run verify:board (34 assertions) now checks the whiteboard itself — the page
is 1280 px by default (from the shipped settings data), the page mode follows the theme line width
(760 px), changing the width setting really changes the page, one page element exists, the bundle and
the DOM contain no card artifacts, wheels scroll to the last paragraph, fit-width puts the whole
page inside the viewport, and the mobile touch gestures keep their anchor invariant.
1.3.4
- The column is 1280 px by default — a desktop web content width. A5 (560 px) turned out to be
the wrong default for reading on a screen: long Chinese lines wrapped early and the column felt
narrow. 1280 px is the width a web page uses, so lines are long, tables have room, and the column
fills a normal screen — which is what makes reading a purely vertical gesture. The slide...
1.3.8
Changelog
1.3.8
- Fixed: double tapping still opened the editor. 1.3.7 only intercepted double taps on images,
but Obsidian's double-tap-to-edit is a gesture of the whole reading view — tapping text
triggers it too, so in practice nothing changed. The interception now covers every double tap
inside the reading view (text, images, diagrams); double tapping an image or diagram opens the
zoomable viewer instead. Two separate switches: 手机:双击不进编辑 and 手机:双击图片 = 放大查看. - Three more real bugs, all found by the browser harness rather than by reading the code:
- The same event was handled twice (the module listens on both
windowanddocumentin
the capture phase), so one tap looked like two and a single tap opened the viewer. Events are now
deduplicated. - A two-finger pinch was counted as a tap, poisoning the next double tap. Multi-touch now
cancels tap tracking, and onlytouchendcounts as a tap (pointerup/clickare used for
swallowing only). - Within one gesture,
touchstarttargets the image buttouchendand the synthetic
dblclicktarget the container, so the hit test concluded "text" and the image never zoomed.
The hit is now resolved from the touch-start element and the coordinates as a fallback.
- The same event was handled twice (the module listens on both
- A touch double tap also produces a synthetic
dblclick(in an unstable order) and both used to
open the viewer; they now share a short window so one double tap opens exactly once. - 手机:双击自检 (off by default) shows a notice for every intercepted double tap — if a gesture
still misbehaves on a real phone, the notice tells whether the plugin saw it at all.
Verification: npm run verify:reading — 28 assertions. Besides the earlier reading-view
checks: a single tap does nothing, a double tap on text does not enter editing and opens nothing,
a double tap on an image opens the viewer exactly once and the simulated editor handler records
zero events, gestures outside the reading view are ignored, and mobile: false (desktop) does
not intercept at all.
1.3.7
- Images follow the line width, scaled by ratio. In the reading view every image is now scaled to
exactly the line width you chose (900 px by default) with `height: auto`, so nothing is cropped
or stretched and a 600 × 200 screenshot becomes 900 × 300. Explicit widths (`![[image.png|300]]`)
are normalised too — one page, one image width, as asked. Two other policies stay available
(fit within the line width, natural size), and the rules only ever touch
`.markdown-reading-view`, never the editor. - Mobile: double-tapping an image zooms it instead of entering editing. In Obsidian's reading
view a double tap on an image means "edit", which is exactly wrong while reading on a phone. The
plugin now intercepts that gesture inside the reading view and opens the zoomable viewer instead;
the edit path is suppressed (use the menu button when you really want to edit). A single tap does
nothing new, gestures outside the reading view are ignored, and desktop is untouched — as
asked, this is a mobile behaviour. - Two ordering bugs found by the browser harness, both fixed rather than papered over. A touch
double tap also produces a synthetic `dblclick`, and which one arrives first is not stable — the
first version opened the viewer twice. Both paths now share a short mutual-exclusion window: one
double tap opens the viewer exactly once. The harness also had to be honest: its simulated
"Obsidian edit" listener originally counted every tap, which proved nothing; it now does real
double-tap detection, so "0 edits" means the double tap was genuinely consumed.
Verification: npm run verify:reading is now 26 assertions — the image policies are measured
in the DOM (fill: 600 px image → 900 px, ratio still 3:1; contain: stays 600 px; natural: no rules
injected), the desktop path is asserted not to open the viewer, and on a phone viewport a real CDP
double tap opens the viewer exactly once while the simulated edit handler records zero events.
1.3.6
- The reading view itself now has a width and a zoom. Obsidian's own reading view is not taken
over — the plugin changes two CSS properties of it: the line width (default 900 px, the value
that was asked for, with 760–1200 px and 跟随主题 / follow-the-theme options) and a zoom level
(60 %–200 %, default 100 %). Your theme files are untouched, and follow the theme + 100 % injects
nothing beyond one `touch-action` line. - Zoom reflows instead of stretching. It is applied as CSS `zoom` with the sizer's `max-width`
divided by the same factor, so the text gets bigger while the measure on screen stays exactly the
line width you picked. A screenshot-level stretch would have changed the line length; this does
not. - You operate it: pinch, Ctrl+wheel, or commands. Pinch inside the reading view on mobile,
Ctrl / ⌘ + wheel on desktop, or the three new commands (阅读视图放大 / 缩小 / 复位到 100 %,
each showing the new level). Only two-finger pinches and Ctrl+wheel are intercepted — one-finger
scrolling, taps and the plain wheel remain Obsidian's. - A real bug found by the browser harness. The first implementation listened for pointer events
and the browser claimed the two-finger gesture for itself: of ten gesture frames only two arrived
(pointercancel). The fix is to listen for `touchstart`/`touchmove` and to declare
`touch-action: pan-y` on the reading view — vertical one-finger scrolling still belongs to
Obsidian, while the two-finger pinch now belongs to the plugin (measured: zoom 1 → 1.86 in one
pinch). - New settings group 阅读视图(Obsidian 自带的页面)/ Reading view, with the three settings above.
Verification: npm run verify:reading — 17 assertions in a real Chromium against a page that
mimics the reading view (a theme width of 720 px, a long document): follow-the-theme really is
720 px with no width rules injected, 900 px really measures 900 px, zoom 150 % keeps the on-screen
measure at 900 px while paragraphs get 1.5x taller, the clamp holds at 60–200 %, Ctrl+wheel zooms
inside the reading view and is ignored outside it (and after the listener is removed), and on a phone
viewport a real CDP two-finger pinch raises the zoom while the throttling keeps the reflow count
below the frame count.
1.3.5
- The card concept is gone. Whiteboard mode used to compile the note into a board of cards:
one card per heading, connectors, folding, a card limit, and a column or flow layout. It is now
what it should have been: one page, 1280 px wide, on a surface you pan, zoom and scroll. The
width setting belongs to the page, not to a card. - Removed along with it:
src/board-model.ts(the heading→card compiler),src/board-layout.ts
(tidy tree / single-column geometry),src/board-render.ts(card DOM, connectors, focus
buttons), the five card settings (排版 / 展开层级 / 间距 / 连线 / 数量上限), every card style in
styles.css, and the 43 unit tests that covered them. The verification suite now asserts the
absence: no card class names or card settings in the built bundle, no card modules in the tree,
and zero card artifacts in the live DOM. - What is left is the part that made the plugin worth having: one page whose width is a setting
(1280 px default, A5/A4 presets one slider step away), recompiled every time you enter the mode and
following the note as you edit, with the same gesture layer as before — drag to pan, pinch to zoom,
适配宽度 to pull the whole page into the viewport and then simply scroll down. - Settings are now: 打开笔记时的默认模式 / default mode and 白板版面宽度 / whiteboard page width.
Verification: npm run verify:board (34 assertions) now checks the whiteboard itself — the page
is 1280 px by default (from the shipped settings data), the page mode follows the theme line width
(760 px), changing the width setting really changes the page, one page element exists, the bundle and
the DOM contain no card artifacts, wheels scroll to the last paragraph, fit-width puts the whole
page inside the viewport, and the mobile touch gestures keep their anchor invariant.
1.3.4
- The column is 1280 px by default — a desktop web content width. A5 (560 px) turned out to be
the wrong default for reading on a screen: long Chinese lines wrapped early and the column felt
narrow. 1280 px is the width a web page uses, so lines are long, tables have room, and the column
fills a normal screen — which is what makes reading a purely vertical gesture. The slider still
names the paper presets (A5 = 148 mm ≈ 560 px, A4 ≈ 794 px) and the readout says
1280 px · 网页宽 Webor560 px · A5 · 148mmso the choice is obvious either way. - 适配栏宽 / fit column width — a new toolbar button that fills the viewport with the column
width, so on a phone (or a small window) you can see a whole line and then just scroll down. The
board still opens at 100% (readable), and you decide when to trade size for the whole column. - Indentation scales with the column: 18 px per level was invisible in a 1280 px column, so it is
now about 3% of the width (≈ 38 px), still capped at three levels.
Verification: npm run verify:board is now 49 assertions — the default is asserted to be
1280 px with the readout naming it (网页宽 Web), A5 is still asserted to be 148 mm ≈ 560 px as a
preset, and the new fit column width assertion measures the real DOM: after one tap the widest card
edge lands inside the viewport and the scale is below 1.
1.3.3
- Two layouts for the whiteboard, and the default is the one you read. The board used to be a
branch tree only: headings in columns with connectors. On a phone that means hunting for content
sideways. The default is now 单栏纵向流 / single column — one A5-wide sheet per section,
stacked in document order, so **scrolling down is reading dow...
1.3.7
Changelog
1.3.7
- Images follow the line width, scaled by ratio. In the reading view every image is now scaled to
exactly the line width you chose (900 px by default) with `height: auto`, so nothing is cropped
or stretched and a 600 × 200 screenshot becomes 900 × 300. Explicit widths (`![[image.png|300]]`)
are normalised too — one page, one image width, as asked. Two other policies stay available
(fit within the line width, natural size), and the rules only ever touch
`.markdown-reading-view`, never the editor. - Mobile: double-tapping an image zooms it instead of entering editing. In Obsidian's reading
view a double tap on an image means "edit", which is exactly wrong while reading on a phone. The
plugin now intercepts that gesture inside the reading view and opens the zoomable viewer instead;
the edit path is suppressed (use the menu button when you really want to edit). A single tap does
nothing new, gestures outside the reading view are ignored, and desktop is untouched — as
asked, this is a mobile behaviour. - Two ordering bugs found by the browser harness, both fixed rather than papered over. A touch
double tap also produces a synthetic `dblclick`, and which one arrives first is not stable — the
first version opened the viewer twice. Both paths now share a short mutual-exclusion window: one
double tap opens the viewer exactly once. The harness also had to be honest: its simulated
"Obsidian edit" listener originally counted every tap, which proved nothing; it now does real
double-tap detection, so "0 edits" means the double tap was genuinely consumed.
Verification: npm run verify:reading is now 26 assertions — the image policies are measured
in the DOM (fill: 600 px image → 900 px, ratio still 3:1; contain: stays 600 px; natural: no rules
injected), the desktop path is asserted not to open the viewer, and on a phone viewport a real CDP
double tap opens the viewer exactly once while the simulated edit handler records zero events.
1.3.6
- The reading view itself now has a width and a zoom. Obsidian's own reading view is not taken
over — the plugin changes two CSS properties of it: the line width (default 900 px, the value
that was asked for, with 760–1200 px and 跟随主题 / follow-the-theme options) and a zoom level
(60 %–200 %, default 100 %). Your theme files are untouched, and follow the theme + 100 % injects
nothing beyond one `touch-action` line. - Zoom reflows instead of stretching. It is applied as CSS `zoom` with the sizer's `max-width`
divided by the same factor, so the text gets bigger while the measure on screen stays exactly the
line width you picked. A screenshot-level stretch would have changed the line length; this does
not. - You operate it: pinch, Ctrl+wheel, or commands. Pinch inside the reading view on mobile,
Ctrl / ⌘ + wheel on desktop, or the three new commands (阅读视图放大 / 缩小 / 复位到 100 %,
each showing the new level). Only two-finger pinches and Ctrl+wheel are intercepted — one-finger
scrolling, taps and the plain wheel remain Obsidian's. - A real bug found by the browser harness. The first implementation listened for pointer events
and the browser claimed the two-finger gesture for itself: of ten gesture frames only two arrived
(pointercancel). The fix is to listen for `touchstart`/`touchmove` and to declare
`touch-action: pan-y` on the reading view — vertical one-finger scrolling still belongs to
Obsidian, while the two-finger pinch now belongs to the plugin (measured: zoom 1 → 1.86 in one
pinch). - New settings group 阅读视图(Obsidian 自带的页面)/ Reading view, with the three settings above.
Verification: npm run verify:reading — 17 assertions in a real Chromium against a page that
mimics the reading view (a theme width of 720 px, a long document): follow-the-theme really is
720 px with no width rules injected, 900 px really measures 900 px, zoom 150 % keeps the on-screen
measure at 900 px while paragraphs get 1.5x taller, the clamp holds at 60–200 %, Ctrl+wheel zooms
inside the reading view and is ignored outside it (and after the listener is removed), and on a phone
viewport a real CDP two-finger pinch raises the zoom while the throttling keeps the reflow count
below the frame count.
1.3.5
- The card concept is gone. Whiteboard mode used to compile the note into a board of cards:
one card per heading, connectors, folding, a card limit, and a column or flow layout. It is now
what it should have been: one page, 1280 px wide, on a surface you pan, zoom and scroll. The
width setting belongs to the page, not to a card. - Removed along with it:
src/board-model.ts(the heading→card compiler),src/board-layout.ts
(tidy tree / single-column geometry),src/board-render.ts(card DOM, connectors, focus
buttons), the five card settings (排版 / 展开层级 / 间距 / 连线 / 数量上限), every card style in
styles.css, and the 43 unit tests that covered them. The verification suite now asserts the
absence: no card class names or card settings in the built bundle, no card modules in the tree,
and zero card artifacts in the live DOM. - What is left is the part that made the plugin worth having: one page whose width is a setting
(1280 px default, A5/A4 presets one slider step away), recompiled every time you enter the mode and
following the note as you edit, with the same gesture layer as before — drag to pan, pinch to zoom,
适配宽度 to pull the whole page into the viewport and then simply scroll down. - Settings are now: 打开笔记时的默认模式 / default mode and 白板版面宽度 / whiteboard page width.
Verification: npm run verify:board (34 assertions) now checks the whiteboard itself — the page
is 1280 px by default (from the shipped settings data), the page mode follows the theme line width
(760 px), changing the width setting really changes the page, one page element exists, the bundle and
the DOM contain no card artifacts, wheels scroll to the last paragraph, fit-width puts the whole
page inside the viewport, and the mobile touch gestures keep their anchor invariant.
1.3.4
- The column is 1280 px by default — a desktop web content width. A5 (560 px) turned out to be
the wrong default for reading on a screen: long Chinese lines wrapped early and the column felt
narrow. 1280 px is the width a web page uses, so lines are long, tables have room, and the column
fills a normal screen — which is what makes reading a purely vertical gesture. The slider still
names the paper presets (A5 = 148 mm ≈ 560 px, A4 ≈ 794 px) and the readout says
1280 px · 网页宽 Webor560 px · A5 · 148mmso the choice is obvious either way. - 适配栏宽 / fit column width — a new toolbar button that fills the viewport with the column
width, so on a phone (or a small window) you can see a whole line and then just scroll down. The
board still opens at 100% (readable), and you decide when to trade size for the whole column. - Indentation scales with the column: 18 px per level was invisible in a 1280 px column, so it is
now about 3% of the width (≈ 38 px), still capped at three levels.
Verification: npm run verify:board is now 49 assertions — the default is asserted to be
1280 px with the readout naming it (网页宽 Web), A5 is still asserted to be 148 mm ≈ 560 px as a
preset, and the new fit column width assertion measures the real DOM: after one tap the widest card
edge lands inside the viewport and the scale is below 1.
1.3.3
- Two layouts for the whiteboard, and the default is the one you read. The board used to be a
branch tree only: headings in columns with connectors. On a phone that means hunting for content
sideways. The default is now 单栏纵向流 / single column — one A5-wide sheet per section,
stacked in document order, so scrolling down is reading down, with indentation (up to three
levels) and the left accent bar carrying the hierarchy instead of connectors. - 分支树 / branch tree is still there for the other job: seeing the whole outline at a glance.
Pick it in the settings (设置 → 白板模式 → 白板排版) or flip it from the view toolbar; connectors
belong to that layout, and the single column draws none (a line inside a one-wide column only
overlaps the cards). - Why cards at all, if it reads like a document? Because the board is zoomable when Obsidian's
reading view is not: each section stays a sheet you can pinch into, tap to bring to the front, and
the sections still fold by heading depth and by card limit. It is a page you can zoom, not a
scroll that cannot.
Verification: npm run verify:board is now 48 assertions. The new ones assert the shipped
default layout is flow (read from the real settings data, not a number copied into the test),
that every card shares the paper width with y strictly increasing in document order, that a
single column draws zero connectors, and — the behavioural one — that scrolling down alone
reaches the last section (the final card enters the viewport). Switching to branch tree flips the
connector assertions back on (count = parent/child relations, endpoints on the card edges), and
switching back drops them again.
1.3.2
- Cards are paper now: A5 by default (148 mm ≈ 560 px). The default card width was 320 px, so a
card held only about twenty Chinese characters per line and the board read like a narrow strip
rather than a page — reported as "the whiteboard looks tiny". A5 is the width a Chinese book line
actually uses (30–40 characters), so it is now the default; A6 and A4 are one slider step away and
the readout shows millimetres and the paper name (560 px ≈ A5 · 148mm). - The board opens at natural size (100%), not fitted. Previously it focused the title card —
which holds a title and a card count and nothing else — so the screen showed one small box in the
middle. Now it opens at 100% with the root card in the top-left corner: one A...
1.3.6
Changelog
1.3.6
- The reading view itself now has a width and a zoom. Obsidian's own reading view is not taken
over — the plugin changes two CSS properties of it: the line width (default 900 px, the value
that was asked for, with 760–1200 px and 跟随主题 / follow-the-theme options) and a zoom level
(60 %–200 %, default 100 %). Your theme files are untouched, and follow the theme + 100 % injects
nothing beyond one `touch-action` line. - Zoom reflows instead of stretching. It is applied as CSS `zoom` with the sizer's `max-width`
divided by the same factor, so the text gets bigger while the measure on screen stays exactly the
line width you picked. A screenshot-level stretch would have changed the line length; this does
not. - You operate it: pinch, Ctrl+wheel, or commands. Pinch inside the reading view on mobile,
Ctrl / ⌘ + wheel on desktop, or the three new commands (阅读视图放大 / 缩小 / 复位到 100 %,
each showing the new level). Only two-finger pinches and Ctrl+wheel are intercepted — one-finger
scrolling, taps and the plain wheel remain Obsidian's. - A real bug found by the browser harness. The first implementation listened for pointer events
and the browser claimed the two-finger gesture for itself: of ten gesture frames only two arrived
(pointercancel). The fix is to listen for `touchstart`/`touchmove` and to declare
`touch-action: pan-y` on the reading view — vertical one-finger scrolling still belongs to
Obsidian, while the two-finger pinch now belongs to the plugin (measured: zoom 1 → 1.86 in one
pinch). - New settings group 阅读视图(Obsidian 自带的页面)/ Reading view, with the three settings above.
Verification: npm run verify:reading — 17 assertions in a real Chromium against a page that
mimics the reading view (a theme width of 720 px, a long document): follow-the-theme really is
720 px with no width rules injected, 900 px really measures 900 px, zoom 150 % keeps the on-screen
measure at 900 px while paragraphs get 1.5x taller, the clamp holds at 60–200 %, Ctrl+wheel zooms
inside the reading view and is ignored outside it (and after the listener is removed), and on a phone
viewport a real CDP two-finger pinch raises the zoom while the throttling keeps the reflow count
below the frame count.
1.3.5
- The card concept is gone. Whiteboard mode used to compile the note into a board of cards:
one card per heading, connectors, folding, a card limit, and a column or flow layout. It is now
what it should have been: one page, 1280 px wide, on a surface you pan, zoom and scroll. The
width setting belongs to the page, not to a card. - Removed along with it:
src/board-model.ts(the heading→card compiler),src/board-layout.ts
(tidy tree / single-column geometry),src/board-render.ts(card DOM, connectors, focus
buttons), the five card settings (排版 / 展开层级 / 间距 / 连线 / 数量上限), every card style in
styles.css, and the 43 unit tests that covered them. The verification suite now asserts the
absence: no card class names or card settings in the built bundle, no card modules in the tree,
and zero card artifacts in the live DOM. - What is left is the part that made the plugin worth having: one page whose width is a setting
(1280 px default, A5/A4 presets one slider step away), recompiled every time you enter the mode and
following the note as you edit, with the same gesture layer as before — drag to pan, pinch to zoom,
适配宽度 to pull the whole page into the viewport and then simply scroll down. - Settings are now: 打开笔记时的默认模式 / default mode and 白板版面宽度 / whiteboard page width.
Verification: npm run verify:board (34 assertions) now checks the whiteboard itself — the page
is 1280 px by default (from the shipped settings data), the page mode follows the theme line width
(760 px), changing the width setting really changes the page, one page element exists, the bundle and
the DOM contain no card artifacts, wheels scroll to the last paragraph, fit-width puts the whole
page inside the viewport, and the mobile touch gestures keep their anchor invariant.
1.3.4
- The column is 1280 px by default — a desktop web content width. A5 (560 px) turned out to be
the wrong default for reading on a screen: long Chinese lines wrapped early and the column felt
narrow. 1280 px is the width a web page uses, so lines are long, tables have room, and the column
fills a normal screen — which is what makes reading a purely vertical gesture. The slider still
names the paper presets (A5 = 148 mm ≈ 560 px, A4 ≈ 794 px) and the readout says
1280 px · 网页宽 Webor560 px · A5 · 148mmso the choice is obvious either way. - 适配栏宽 / fit column width — a new toolbar button that fills the viewport with the column
width, so on a phone (or a small window) you can see a whole line and then just scroll down. The
board still opens at 100% (readable), and you decide when to trade size for the whole column. - Indentation scales with the column: 18 px per level was invisible in a 1280 px column, so it is
now about 3% of the width (≈ 38 px), still capped at three levels.
Verification: npm run verify:board is now 49 assertions — the default is asserted to be
1280 px with the readout naming it (网页宽 Web), A5 is still asserted to be 148 mm ≈ 560 px as a
preset, and the new fit column width assertion measures the real DOM: after one tap the widest card
edge lands inside the viewport and the scale is below 1.
1.3.3
- Two layouts for the whiteboard, and the default is the one you read. The board used to be a
branch tree only: headings in columns with connectors. On a phone that means hunting for content
sideways. The default is now 单栏纵向流 / single column — one A5-wide sheet per section,
stacked in document order, so scrolling down is reading down, with indentation (up to three
levels) and the left accent bar carrying the hierarchy instead of connectors. - 分支树 / branch tree is still there for the other job: seeing the whole outline at a glance.
Pick it in the settings (设置 → 白板模式 → 白板排版) or flip it from the view toolbar; connectors
belong to that layout, and the single column draws none (a line inside a one-wide column only
overlaps the cards). - Why cards at all, if it reads like a document? Because the board is zoomable when Obsidian's
reading view is not: each section stays a sheet you can pinch into, tap to bring to the front, and
the sections still fold by heading depth and by card limit. It is a page you can zoom, not a
scroll that cannot.
Verification: npm run verify:board is now 48 assertions. The new ones assert the shipped
default layout is flow (read from the real settings data, not a number copied into the test),
that every card shares the paper width with y strictly increasing in document order, that a
single column draws zero connectors, and — the behavioural one — that scrolling down alone
reaches the last section (the final card enters the viewport). Switching to branch tree flips the
connector assertions back on (count = parent/child relations, endpoints on the card edges), and
switching back drops them again.
1.3.2
- Cards are paper now: A5 by default (148 mm ≈ 560 px). The default card width was 320 px, so a
card held only about twenty Chinese characters per line and the board read like a narrow strip
rather than a page — reported as "the whiteboard looks tiny". A5 is the width a Chinese book line
actually uses (30–40 characters), so it is now the default; A6 and A4 are one slider step away and
the readout shows millimetres and the paper name (560 px ≈ A5 · 148mm). - The board opens at natural size (100%), not fitted. Previously it focused the title card —
which holds a title and a card count and nothing else — so the screen showed one small box in the
middle. Now it opens at 100% with the root card in the top-left corner: one A5 page at reading
size. The board still zooms and pans, and 回到全图 / 回到标题卡 are still in the toolbar. - Parents are top-aligned with their children, not centred against them. A tidy tree centres a
parent against its children band; on a document board that pushes a short title card into the
middle of the screen and leaves the top half empty — the other half of "it looks tiny". Top
aligned, the top-left corner is content and you read downward, like paper. - The whiteboard is a view: it recompiles every time. Entering whiteboard mode compiles the
Markdown again from scratch, reading the live editor text when the note is open in a pane (so
unsaved edits show up), and it recompiles again when the note changes — a save, or typing in
another pane, throttled to 400 ms. Nothing is written back, and there is a new
重新编译 / recompile button in the toolbar for a forced refresh. - Board positions are now keyed by card width: change the paper size and the saved zoom/pan is
dropped rather than restored onto a board whose geometry no longer matches.
Verification: npm run verify:board is now 42 assertions — the new ones assert the shipped
default is A5 (560 px ≈ A5 · 148mm, from the real settings data, not a number copied into the
test), that opening lands at scale 1 with content within one margin of the top-left corner, and that
every card's real DOM width equals the configured paper width.
1.3.1
- Fixed: opening a note as a whiteboard showed an empty view. Obsidian's view lifecycle is
onOpen()and thensetState().onOpen()reads the configured default mode (page) and
paints the mode classes on the root element;setState()then switches the mode to whiteboard
because the command asked for it — but nothing repainted the classes. The board was rendered into
.zr-board-host, which.zr-mode-page .zr-board-host { display: none }hides, and the visible
layer was the empty page. Symptom: run 编译成白板 / open as a whiteboard and see nothing at all
(setting the d...
1.3.5
Changelog
1.3.5
- The card concept is gone. Whiteboard mode used to compile the note into a board of cards:
one card per heading, connectors, folding, a card limit, and a column or flow layout. It is now
what it should have been: one page, 1280 px wide, on a surface you pan, zoom and scroll. The
width setting belongs to the page, not to a card. - Removed along with it:
src/board-model.ts(the heading→card compiler),src/board-layout.ts
(tidy tree / single-column geometry),src/board-render.ts(card DOM, connectors, focus
buttons), the five card settings (排版 / 展开层级 / 间距 / 连线 / 数量上限), every card style in
styles.css, and the 43 unit tests that covered them. The verification suite now asserts the
absence: no card class names or card settings in the built bundle, no card modules in the tree,
and zero card artifacts in the live DOM. - What is left is the part that made the plugin worth having: one page whose width is a setting
(1280 px default, A5/A4 presets one slider step away), recompiled every time you enter the mode and
following the note as you edit, with the same gesture layer as before — drag to pan, pinch to zoom,
适配宽度 to pull the whole page into the viewport and then simply scroll down. - Settings are now: 打开笔记时的默认模式 / default mode and 白板版面宽度 / whiteboard page width.
Verification: npm run verify:board (34 assertions) now checks the whiteboard itself — the page
is 1280 px by default (from the shipped settings data), the page mode follows the theme line width
(760 px), changing the width setting really changes the page, one page element exists, the bundle and
the DOM contain no card artifacts, wheels scroll to the last paragraph, fit-width puts the whole
page inside the viewport, and the mobile touch gestures keep their anchor invariant.
1.3.4
- The column is 1280 px by default — a desktop web content width. A5 (560 px) turned out to be
the wrong default for reading on a screen: long Chinese lines wrapped early and the column felt
narrow. 1280 px is the width a web page uses, so lines are long, tables have room, and the column
fills a normal screen — which is what makes reading a purely vertical gesture. The slider still
names the paper presets (A5 = 148 mm ≈ 560 px, A4 ≈ 794 px) and the readout says
1280 px · 网页宽 Webor560 px · A5 · 148mmso the choice is obvious either way. - 适配栏宽 / fit column width — a new toolbar button that fills the viewport with the column
width, so on a phone (or a small window) you can see a whole line and then just scroll down. The
board still opens at 100% (readable), and you decide when to trade size for the whole column. - Indentation scales with the column: 18 px per level was invisible in a 1280 px column, so it is
now about 3% of the width (≈ 38 px), still capped at three levels.
Verification: npm run verify:board is now 49 assertions — the default is asserted to be
1280 px with the readout naming it (网页宽 Web), A5 is still asserted to be 148 mm ≈ 560 px as a
preset, and the new fit column width assertion measures the real DOM: after one tap the widest card
edge lands inside the viewport and the scale is below 1.
1.3.3
- Two layouts for the whiteboard, and the default is the one you read. The board used to be a
branch tree only: headings in columns with connectors. On a phone that means hunting for content
sideways. The default is now 单栏纵向流 / single column — one A5-wide sheet per section,
stacked in document order, so scrolling down is reading down, with indentation (up to three
levels) and the left accent bar carrying the hierarchy instead of connectors. - 分支树 / branch tree is still there for the other job: seeing the whole outline at a glance.
Pick it in the settings (设置 → 白板模式 → 白板排版) or flip it from the view toolbar; connectors
belong to that layout, and the single column draws none (a line inside a one-wide column only
overlaps the cards). - Why cards at all, if it reads like a document? Because the board is zoomable when Obsidian's
reading view is not: each section stays a sheet you can pinch into, tap to bring to the front, and
the sections still fold by heading depth and by card limit. It is a page you can zoom, not a
scroll that cannot.
Verification: npm run verify:board is now 48 assertions. The new ones assert the shipped
default layout is flow (read from the real settings data, not a number copied into the test),
that every card shares the paper width with y strictly increasing in document order, that a
single column draws zero connectors, and — the behavioural one — that scrolling down alone
reaches the last section (the final card enters the viewport). Switching to branch tree flips the
connector assertions back on (count = parent/child relations, endpoints on the card edges), and
switching back drops them again.
1.3.2
- Cards are paper now: A5 by default (148 mm ≈ 560 px). The default card width was 320 px, so a
card held only about twenty Chinese characters per line and the board read like a narrow strip
rather than a page — reported as "the whiteboard looks tiny". A5 is the width a Chinese book line
actually uses (30–40 characters), so it is now the default; A6 and A4 are one slider step away and
the readout shows millimetres and the paper name (560 px ≈ A5 · 148mm). - The board opens at natural size (100%), not fitted. Previously it focused the title card —
which holds a title and a card count and nothing else — so the screen showed one small box in the
middle. Now it opens at 100% with the root card in the top-left corner: one A5 page at reading
size. The board still zooms and pans, and 回到全图 / 回到标题卡 are still in the toolbar. - Parents are top-aligned with their children, not centred against them. A tidy tree centres a
parent against its children band; on a document board that pushes a short title card into the
middle of the screen and leaves the top half empty — the other half of "it looks tiny". Top
aligned, the top-left corner is content and you read downward, like paper. - The whiteboard is a view: it recompiles every time. Entering whiteboard mode compiles the
Markdown again from scratch, reading the live editor text when the note is open in a pane (so
unsaved edits show up), and it recompiles again when the note changes — a save, or typing in
another pane, throttled to 400 ms. Nothing is written back, and there is a new
重新编译 / recompile button in the toolbar for a forced refresh. - Board positions are now keyed by card width: change the paper size and the saved zoom/pan is
dropped rather than restored onto a board whose geometry no longer matches.
Verification: npm run verify:board is now 42 assertions — the new ones assert the shipped
default is A5 (560 px ≈ A5 · 148mm, from the real settings data, not a number copied into the
test), that opening lands at scale 1 with content within one margin of the top-left corner, and that
every card's real DOM width equals the configured paper width.
1.3.1
- Fixed: opening a note as a whiteboard showed an empty view. Obsidian's view lifecycle is
onOpen()and thensetState().onOpen()reads the configured default mode (page) and
paints the mode classes on the root element;setState()then switches the mode to whiteboard
because the command asked for it — but nothing repainted the classes. The board was rendered into
.zr-board-host, which.zr-mode-page .zr-board-host { display: none }hides, and the visible
layer was the empty page. Symptom: run 编译成白板 / open as a whiteboard and see nothing at all
(setting the default mode to whiteboard worked, because thenonOpen()painted the right classes). - The mode classes are now applied inside the render path (
renderFile()), not merely while
building the UI: whichever layer is about to be filled is made visible first. The rule is written
down next to the code, because "visibility is not a one-time setup" is exactly the sort of
invariant that silently rots. npm run verify:boardnow reproduces the bug before asserting the fix (35 assertions): it
paints the page classes, renders the board without re-aligning them, and asserts that exactly
0 cards are visible — then re-renders through the real path and asserts all of them are. A test
that cannot fail is not a test.- Board rendering failures are no longer silent: a caught error raises a Notice (and a console
error) instead of leaving a blank view — on a phone there is no console to open. - Keyboard shortcuts (
+-0) are registered once per view instead of once per UI rebuild,
so toggling the mode no longer handles the same key twice.
1.3.0
- Whiteboard mode: the note is compiled into a board, instead of being put on one. The previous
version rendered the whole note as one tall page inside a zoomable surface. This version adds the
other reading model: the Markdown itself becomes the board. Every heading turns into a card
holding that section's content, frontmatter is dropped (it is metadata, not the note), and code
fences, tables, callouts, quotes, math, images and Mermaid blocks become typed blocks inside their
card — a#inside a code fence is deliberately not a heading. Cards are laid out as a tidy
tree (children in the next column, vertically centred against their parent) with bezier
connectors from the parent's right edge to the child's left edge, so the outline is visible at a
glance on a phone and readable after a pinch. - Heights come from the real DOM, twice. Estimating Chinese text height is guesswork, so the
board is laid out once from an estimate (the board appears immediately) and again from measured
offsetHeightvalues. Tables, images and long paragraphs then take exactly the room they need,
and no card can overlap another. - **Deep headings fold into their ancestor...
1.3.4
Changelog
1.3.4
- The column is 1280 px by default — a desktop web content width. A5 (560 px) turned out to be
the wrong default for reading on a screen: long Chinese lines wrapped early and the column felt
narrow. 1280 px is the width a web page uses, so lines are long, tables have room, and the column
fills a normal screen — which is what makes reading a purely vertical gesture. The slider still
names the paper presets (A5 = 148 mm ≈ 560 px, A4 ≈ 794 px) and the readout says
1280 px · 网页宽 Webor560 px · A5 · 148mmso the choice is obvious either way. - 适配栏宽 / fit column width — a new toolbar button that fills the viewport with the column
width, so on a phone (or a small window) you can see a whole line and then just scroll down. The
board still opens at 100% (readable), and you decide when to trade size for the whole column. - Indentation scales with the column: 18 px per level was invisible in a 1280 px column, so it is
now about 3% of the width (≈ 38 px), still capped at three levels.
Verification: npm run verify:board is now 49 assertions — the default is asserted to be
1280 px with the readout naming it (网页宽 Web), A5 is still asserted to be 148 mm ≈ 560 px as a
preset, and the new fit column width assertion measures the real DOM: after one tap the widest card
edge lands inside the viewport and the scale is below 1.
1.3.3
- Two layouts for the whiteboard, and the default is the one you read. The board used to be a
branch tree only: headings in columns with connectors. On a phone that means hunting for content
sideways. The default is now 单栏纵向流 / single column — one A5-wide sheet per section,
stacked in document order, so scrolling down is reading down, with indentation (up to three
levels) and the left accent bar carrying the hierarchy instead of connectors. - 分支树 / branch tree is still there for the other job: seeing the whole outline at a glance.
Pick it in the settings (设置 → 白板模式 → 白板排版) or flip it from the view toolbar; connectors
belong to that layout, and the single column draws none (a line inside a one-wide column only
overlaps the cards). - Why cards at all, if it reads like a document? Because the board is zoomable when Obsidian's
reading view is not: each section stays a sheet you can pinch into, tap to bring to the front, and
the sections still fold by heading depth and by card limit. It is a page you can zoom, not a
scroll that cannot.
Verification: npm run verify:board is now 48 assertions. The new ones assert the shipped
default layout is flow (read from the real settings data, not a number copied into the test),
that every card shares the paper width with y strictly increasing in document order, that a
single column draws zero connectors, and — the behavioural one — that scrolling down alone
reaches the last section (the final card enters the viewport). Switching to branch tree flips the
connector assertions back on (count = parent/child relations, endpoints on the card edges), and
switching back drops them again.
1.3.2
- Cards are paper now: A5 by default (148 mm ≈ 560 px). The default card width was 320 px, so a
card held only about twenty Chinese characters per line and the board read like a narrow strip
rather than a page — reported as "the whiteboard looks tiny". A5 is the width a Chinese book line
actually uses (30–40 characters), so it is now the default; A6 and A4 are one slider step away and
the readout shows millimetres and the paper name (560 px ≈ A5 · 148mm). - The board opens at natural size (100%), not fitted. Previously it focused the title card —
which holds a title and a card count and nothing else — so the screen showed one small box in the
middle. Now it opens at 100% with the root card in the top-left corner: one A5 page at reading
size. The board still zooms and pans, and 回到全图 / 回到标题卡 are still in the toolbar. - Parents are top-aligned with their children, not centred against them. A tidy tree centres a
parent against its children band; on a document board that pushes a short title card into the
middle of the screen and leaves the top half empty — the other half of "it looks tiny". Top
aligned, the top-left corner is content and you read downward, like paper. - The whiteboard is a view: it recompiles every time. Entering whiteboard mode compiles the
Markdown again from scratch, reading the live editor text when the note is open in a pane (so
unsaved edits show up), and it recompiles again when the note changes — a save, or typing in
another pane, throttled to 400 ms. Nothing is written back, and there is a new
重新编译 / recompile button in the toolbar for a forced refresh. - Board positions are now keyed by card width: change the paper size and the saved zoom/pan is
dropped rather than restored onto a board whose geometry no longer matches.
Verification: npm run verify:board is now 42 assertions — the new ones assert the shipped
default is A5 (560 px ≈ A5 · 148mm, from the real settings data, not a number copied into the
test), that opening lands at scale 1 with content within one margin of the top-left corner, and that
every card's real DOM width equals the configured paper width.
1.3.1
- Fixed: opening a note as a whiteboard showed an empty view. Obsidian's view lifecycle is
onOpen()and thensetState().onOpen()reads the configured default mode (page) and
paints the mode classes on the root element;setState()then switches the mode to whiteboard
because the command asked for it — but nothing repainted the classes. The board was rendered into
.zr-board-host, which.zr-mode-page .zr-board-host { display: none }hides, and the visible
layer was the empty page. Symptom: run 编译成白板 / open as a whiteboard and see nothing at all
(setting the default mode to whiteboard worked, because thenonOpen()painted the right classes). - The mode classes are now applied inside the render path (
renderFile()), not merely while
building the UI: whichever layer is about to be filled is made visible first. The rule is written
down next to the code, because "visibility is not a one-time setup" is exactly the sort of
invariant that silently rots. npm run verify:boardnow reproduces the bug before asserting the fix (35 assertions): it
paints the page classes, renders the board without re-aligning them, and asserts that exactly
0 cards are visible — then re-renders through the real path and asserts all of them are. A test
that cannot fail is not a test.- Board rendering failures are no longer silent: a caught error raises a Notice (and a console
error) instead of leaving a blank view — on a phone there is no console to open. - Keyboard shortcuts (
+-0) are registered once per view instead of once per UI rebuild,
so toggling the mode no longer handles the same key twice.
1.3.0
- Whiteboard mode: the note is compiled into a board, instead of being put on one. The previous
version rendered the whole note as one tall page inside a zoomable surface. This version adds the
other reading model: the Markdown itself becomes the board. Every heading turns into a card
holding that section's content, frontmatter is dropped (it is metadata, not the note), and code
fences, tables, callouts, quotes, math, images and Mermaid blocks become typed blocks inside their
card — a#inside a code fence is deliberately not a heading. Cards are laid out as a tidy
tree (children in the next column, vertically centred against their parent) with bezier
connectors from the parent's right edge to the child's left edge, so the outline is visible at a
glance on a phone and readable after a pinch. - Heights come from the real DOM, twice. Estimating Chinese text height is guesswork, so the
board is laid out once from an estimate (the board appears immediately) and again from measured
offsetHeightvalues. Tables, images and long paragraphs then take exactly the room they need,
and no card can overlap another. - Deep headings fold into their ancestor card. Chinese notes often run four or five heading
levels deep, and one column per level turns the board into a thin ribbon: sections deeper than the
configured depth are folded in as subsections. A card limit folds deeper automatically, so a note
with hundreds of headings cannot stall a phone, and the root card says how many cards there are. - Tap a card title to bring that card to the front, 回到全图 fits the whole board, 回到标题卡
returns to the root. Links inside a card keep their normal meaning: only the card head and its
focus button zoom. The board transition is 240 ms, and it is cancelled the moment a pointer goes
down so dragging never feels laggy. - The settings page is bilingual and searchable. Every setting now reads 中文 / English — name,
description and the options of every dropdown — and on Obsidian 1.13+ the page is declared through
getSettingDefinitions(), so settings appear in the settings search: typing 双指 or pinch
jumps straight to the two-finger switch (双指捏合缩放 / Two-finger pinch zoom), which is exactly
what could not be found before. The content lives in one data module (src/settings-spec.ts) and is
rendered twice — declaratively on 1.13+, imperatively on 1.5.7–1.12 — so older versions get the
same page. A new 手势速查 / gesture cheat sheet lists every gesture in both languages. - Two-finger pinch is now a setting, with a sensitivity slider. Turning it off leaves two fingers
panning with their midpoint (handy when an IME or stylus emits a phantom second pointer); the
slider scales the pinch ratio between 0.5x and 2x. - The zoom button on images and diagrams moved to the top-left corner by default. The top-right
corner is where Obsidian puts its own 编辑源文件 / edit source and more-options controls, and the
button used to cover...
1.3.3
Changelog
1.3.3
- Two layouts for the whiteboard, and the default is the one you read. The board used to be a
branch tree only: headings in columns with connectors. On a phone that means hunting for content
sideways. The default is now 单栏纵向流 / single column — one A5-wide sheet per section,
stacked in document order, so scrolling down is reading down, with indentation (up to three
levels) and the left accent bar carrying the hierarchy instead of connectors. - 分支树 / branch tree is still there for the other job: seeing the whole outline at a glance.
Pick it in the settings (设置 → 白板模式 → 白板排版) or flip it from the view toolbar; connectors
belong to that layout, and the single column draws none (a line inside a one-wide column only
overlaps the cards). - Why cards at all, if it reads like a document? Because the board is zoomable when Obsidian's
reading view is not: each section stays a sheet you can pinch into, tap to bring to the front, and
the sections still fold by heading depth and by card limit. It is a page you can zoom, not a
scroll that cannot.
Verification: npm run verify:board is now 48 assertions. The new ones assert the shipped
default layout is flow (read from the real settings data, not a number copied into the test),
that every card shares the paper width with y strictly increasing in document order, that a
single column draws zero connectors, and — the behavioural one — that scrolling down alone
reaches the last section (the final card enters the viewport). Switching to branch tree flips the
connector assertions back on (count = parent/child relations, endpoints on the card edges), and
switching back drops them again.
1.3.2
- Cards are paper now: A5 by default (148 mm ≈ 560 px). The default card width was 320 px, so a
card held only about twenty Chinese characters per line and the board read like a narrow strip
rather than a page — reported as "the whiteboard looks tiny". A5 is the width a Chinese book line
actually uses (30–40 characters), so it is now the default; A6 and A4 are one slider step away and
the readout shows millimetres and the paper name (560 px ≈ A5 · 148mm). - The board opens at natural size (100%), not fitted. Previously it focused the title card —
which holds a title and a card count and nothing else — so the screen showed one small box in the
middle. Now it opens at 100% with the root card in the top-left corner: one A5 page at reading
size. The board still zooms and pans, and 回到全图 / 回到标题卡 are still in the toolbar. - Parents are top-aligned with their children, not centred against them. A tidy tree centres a
parent against its children band; on a document board that pushes a short title card into the
middle of the screen and leaves the top half empty — the other half of "it looks tiny". Top
aligned, the top-left corner is content and you read downward, like paper. - The whiteboard is a view: it recompiles every time. Entering whiteboard mode compiles the
Markdown again from scratch, reading the live editor text when the note is open in a pane (so
unsaved edits show up), and it recompiles again when the note changes — a save, or typing in
another pane, throttled to 400 ms. Nothing is written back, and there is a new
重新编译 / recompile button in the toolbar for a forced refresh. - Board positions are now keyed by card width: change the paper size and the saved zoom/pan is
dropped rather than restored onto a board whose geometry no longer matches.
Verification: npm run verify:board is now 42 assertions — the new ones assert the shipped
default is A5 (560 px ≈ A5 · 148mm, from the real settings data, not a number copied into the
test), that opening lands at scale 1 with content within one margin of the top-left corner, and that
every card's real DOM width equals the configured paper width.
1.3.1
- Fixed: opening a note as a whiteboard showed an empty view. Obsidian's view lifecycle is
onOpen()and thensetState().onOpen()reads the configured default mode (page) and
paints the mode classes on the root element;setState()then switches the mode to whiteboard
because the command asked for it — but nothing repainted the classes. The board was rendered into
.zr-board-host, which.zr-mode-page .zr-board-host { display: none }hides, and the visible
layer was the empty page. Symptom: run 编译成白板 / open as a whiteboard and see nothing at all
(setting the default mode to whiteboard worked, because thenonOpen()painted the right classes). - The mode classes are now applied inside the render path (
renderFile()), not merely while
building the UI: whichever layer is about to be filled is made visible first. The rule is written
down next to the code, because "visibility is not a one-time setup" is exactly the sort of
invariant that silently rots. npm run verify:boardnow reproduces the bug before asserting the fix (35 assertions): it
paints the page classes, renders the board without re-aligning them, and asserts that exactly
0 cards are visible — then re-renders through the real path and asserts all of them are. A test
that cannot fail is not a test.- Board rendering failures are no longer silent: a caught error raises a Notice (and a console
error) instead of leaving a blank view — on a phone there is no console to open. - Keyboard shortcuts (
+-0) are registered once per view instead of once per UI rebuild,
so toggling the mode no longer handles the same key twice.
1.3.0
- Whiteboard mode: the note is compiled into a board, instead of being put on one. The previous
version rendered the whole note as one tall page inside a zoomable surface. This version adds the
other reading model: the Markdown itself becomes the board. Every heading turns into a card
holding that section's content, frontmatter is dropped (it is metadata, not the note), and code
fences, tables, callouts, quotes, math, images and Mermaid blocks become typed blocks inside their
card — a#inside a code fence is deliberately not a heading. Cards are laid out as a tidy
tree (children in the next column, vertically centred against their parent) with bezier
connectors from the parent's right edge to the child's left edge, so the outline is visible at a
glance on a phone and readable after a pinch. - Heights come from the real DOM, twice. Estimating Chinese text height is guesswork, so the
board is laid out once from an estimate (the board appears immediately) and again from measured
offsetHeightvalues. Tables, images and long paragraphs then take exactly the room they need,
and no card can overlap another. - Deep headings fold into their ancestor card. Chinese notes often run four or five heading
levels deep, and one column per level turns the board into a thin ribbon: sections deeper than the
configured depth are folded in as subsections. A card limit folds deeper automatically, so a note
with hundreds of headings cannot stall a phone, and the root card says how many cards there are. - Tap a card title to bring that card to the front, 回到全图 fits the whole board, 回到标题卡
returns to the root. Links inside a card keep their normal meaning: only the card head and its
focus button zoom. The board transition is 240 ms, and it is cancelled the moment a pointer goes
down so dragging never feels laggy. - The settings page is bilingual and searchable. Every setting now reads 中文 / English — name,
description and the options of every dropdown — and on Obsidian 1.13+ the page is declared through
getSettingDefinitions(), so settings appear in the settings search: typing 双指 or pinch
jumps straight to the two-finger switch (双指捏合缩放 / Two-finger pinch zoom), which is exactly
what could not be found before. The content lives in one data module (src/settings-spec.ts) and is
rendered twice — declaratively on 1.13+, imperatively on 1.5.7–1.12 — so older versions get the
same page. A new 手势速查 / gesture cheat sheet lists every gesture in both languages. - Two-finger pinch is now a setting, with a sensitivity slider. Turning it off leaves two fingers
panning with their midpoint (handy when an IME or stylus emits a phantom second pointer); the
slider scales the pinch ratio between 0.5x and 2x. - The zoom button on images and diagrams moved to the top-left corner by default. The top-right
corner is where Obsidian puts its own 编辑源文件 / edit source and more-options controls, and the
button used to cover them (reported from a real phone). The corner is a setting, so anyone who
preferred the old position can put it back — and both placement paths (persistent inline button and
hover singleton) follow it. - New settings: 打开笔记时的默认模式 / default mode (page or whiteboard), 卡片宽度 / card width,
卡片展开层级 / outline depth, 卡片间距 / card spacing, 显示连线 / connectors, 卡片数量上限 /
card limit, 放大按钮的位置 / zoom button corner. - Zoom and position are now remembered per mode (page and whiteboard have different sizes, so
sharing one transform made them fight). Whiteboard positions use the samedata.json, with a
#boardsuffix. - Whiteboard mode is reachable from the command palette, the ribbon icon, the view toolbar, and the
file-list context menu (编译成白板 / open as a whiteboard) — the last one so a phone user can go
straight from the file list into a board without opening the note first.
Verification: npm test now has 84 unit tests, including the board compiler (fenced code,
frontmatter, table detection, heading level jumps, folding) and a contract test that fails if any
setting is missing its Chinese or English half. npm run verify:board is new: 32 assertions in a
real Chromium, asserting the geometry from the DOM — cards never overlap, every card is inside the
board bounds, every connector lands on its cards' edges, the measured height equals the layout
height (t...