fix(docs): keep Mermaid wheel zoom anchored to the pointer
This commit is contained in:
1 parent
b0ec8cef32
commit
cd0cc91382
6 files changed
+12
-13
No files matched your search
@@ -2,5 +2,5 @@
|
||||
# side as of the last confirmed-consistent state. Both languages carry equal authority;
|
||||
# after editing either side, bring the other along and re-record with:
|
||||
# pnpm run verify-translation-pairing --write .agents/notes/implemented/feature/2026-09-14-docs-mermaid-viewer.md
|
||||
2026-09-14-docs-mermaid-viewer.md: fa51cf527e126e21ba46c72d980dcfc28c2d16e6
|
||||
2026-09-14-docs-mermaid-viewer.zh.md: 9e50cabe524877c0aea6c734c1cb613c99ea9e02
|
||||
2026-09-14-docs-mermaid-viewer.md: 3c014000bccd25908f699333efbcb551c1ee1599
|
||||
2026-09-14-docs-mermaid-viewer.zh.md: 48ce642b312cd8881fda437e8cb9ee93e21698ef
|
||||
@@ -12,7 +12,7 @@ Complex Mermaid diagrams lose readable detail when scaled to the documentation c
|
||||
|
||||
The [VitePress theme](../../../../website/.vitepress/theme/index.ts) adds a fullscreen entry above each rendered Mermaid SVG. A native modal dialog provides an inert background and Escape dismissal; the toolbar cycles keyboard focus through zoom, fit, and close controls. Panzoom supplies pointer, wheel, and pinch interaction. Arrow keys pan in fixed screen distances. Closing restores the entry's focus without scrolling and restores the page's previous overflow setting.
|
||||
|
||||
The viewer copies the SVG into a shadow root. Mermaid's embedded selectors and fragment IDs stay local to the copy, so its markers and styles cannot resolve against the original diagram. The natural viewBox dimensions determine the canvas size; the initial scale and every resize fit the entire diagram without enlarging it beyond its natural size. This preserves vector detail while keeping fit independent of the narrow document column.
|
||||
The viewer copies the SVG into a shadow root. Mermaid's embedded selectors and fragment IDs stay local to the copy, so its markers and styles cannot resolve against the original diagram. Panzoom transforms a viewport-sized canvas containing the SVG at its natural viewBox dimensions, so pointer coordinates and the transform origin share the same center; the initial scale and every resize fit the entire diagram without enlarging it beyond its natural size. This preserves vector detail while keeping fit independent of the narrow document column.
|
||||
|
||||
Viewer resources belong to the mounted theme. Route, language, theme, and source-SVG replacement close the active view; asynchronous Mermaid renders receive a fresh entry. The scroll lock belongs on the body because the Mermaid plugin observes every attribute change on the HTML element and rerenders in response. The [implementation](../../../../website/.vitepress/theme/mermaid-viewer.ts) leaves Markdown, raw page copies, and `llms.txt` generation with their existing owners.
|
||||
|
||||
|
||||
@@ -12,7 +12,7 @@ Status: implemented
|
||||
|
||||
[VitePress 主题](../../../../website/.vitepress/theme/index.ts) 在每张已渲染的 Mermaid SVG 上方增加全屏入口。原生模态对话框使背景不可交互,并支持 Escape 退出;工具栏将键盘焦点循环限定在缩放、适应窗口和关闭控件之间。Panzoom 提供指针、滚轮和双指交互。方向键按固定的屏幕距离平移。关闭时恢复入口焦点且不滚动页面,并恢复页面原有的 overflow 设置。
|
||||
|
||||
查看器将 SVG 复制到 shadow root 中。Mermaid 内嵌的选择器和片段 ID 限定在副本内部,因此副本的标记和样式不会解析到原始图表上。viewBox 的自然尺寸决定画布大小;初始缩放以及每次窗口尺寸变化都会适配整张图表,且不会放大到超出自然尺寸。这样既保留矢量细节,也使适配不受文档窄列宽度的影响。
|
||||
查看器将 SVG 复制到 shadow root 中。Mermaid 内嵌的选择器和片段 ID 限定在副本内部,因此副本的标记和样式不会解析到原始图表上。Panzoom 变换与视口等大的画布,其中的 SVG 使用 viewBox 的自然尺寸,使指针坐标与变换原点共享同一个中心;初始缩放以及每次窗口尺寸变化都会适配整张图表,且不会放大到超出自然尺寸。这样既保留矢量细节,也使适配不受文档窄列宽度的影响。
|
||||
|
||||
查看器资源由已挂载的主题持有。路由、语言、主题和源 SVG 替换都会关闭当前视图;异步渲染的 Mermaid 图表会获得新的入口。滚动锁设置在 body 上,因为 Mermaid 插件会观察 HTML 元素的每次属性变化并重新渲染。[实现](../../../../website/.vitepress/theme/mermaid-viewer.ts) 将 Markdown、原始页面副本和 `llms.txt` 的生成保留在原有归属处。
|
||||
|
||||
|
||||
@@ -79,8 +79,7 @@
|
||||
|
||||
.dsh-diagram-paper {
|
||||
position: absolute;
|
||||
left: 50%;
|
||||
top: 50%;
|
||||
inset: 0;
|
||||
}
|
||||
|
||||
@media (max-width: 600px) {
|
||||
|
||||
@@ -63,15 +63,14 @@ function openDiagram(
|
||||
viewport.className = 'dsh-diagram-viewport'
|
||||
const paper = doc.createElement('div')
|
||||
paper.className = 'dsh-diagram-paper'
|
||||
Object.assign(paper.style, {
|
||||
width: `${size.width}px`, height: `${size.height}px`,
|
||||
marginLeft: `${-size.width / 2}px`, marginTop: `${-size.height / 2}px`,
|
||||
})
|
||||
// Each Mermaid SVG embeds ID-scoped styles and fragment references. A shadow root
|
||||
// keeps the enlarged copy's IDs and styles separate from the original diagram.
|
||||
const shadow = paper.attachShadow({ mode: 'open' })
|
||||
const clone = svg.cloneNode(true) as SVGSVGElement
|
||||
Object.assign(clone.style, { width: '100%', height: '100%', maxWidth: 'none', display: 'block' })
|
||||
Object.assign(clone.style, {
|
||||
position: 'absolute', left: '50%', top: '50%', transform: 'translate(-50%, -50%)',
|
||||
width: `${size.width}px`, height: `${size.height}px`, maxWidth: 'none', display: 'block',
|
||||
})
|
||||
shadow.append(clone)
|
||||
viewport.append(paper)
|
||||
dialog.append(toolbar, help, viewport)
|
||||
|
||||
@@ -103,8 +103,9 @@ describe('documentation Mermaid viewer', () => {
|
||||
const dialog = open()
|
||||
expect(panzoom.create.mock.calls[0]?.[1]).toMatchObject({ startScale: 0.2, minScale: 0.1 })
|
||||
const paper = required(dialog.querySelector<HTMLElement>('.dsh-diagram-paper'))
|
||||
expect(paper.style.width).toBe('2000px')
|
||||
expect(paper.style.height).toBe('3000px')
|
||||
const clone = required(paper.shadowRoot?.querySelector('svg'))
|
||||
expect(clone.style.width).toBe('2000px')
|
||||
expect(clone.style.height).toBe('3000px')
|
||||
expect(paper.shadowRoot?.querySelector('path')?.getAttribute('marker-end')).toBe('url(#arrow)')
|
||||
expect(document.querySelectorAll('#diagram')).toHaveLength(1)
|
||||
expect(source.outerHTML).toBe(original)
|
||||
|
||||
Reference in new issue
Block a user