fix(mermaid): themechange detector + expand simplification
This commit is contained in:
		
							parent
							
								
									e59181c3aa
								
							
						
					
					
						commit
						87b803790c
					
				| @ -12,7 +12,8 @@ class DiagramPanZoom { | ||||
|   private scale = 1 | ||||
|   private readonly MIN_SCALE = 0.5 | ||||
|   private readonly MAX_SCALE = 3 | ||||
|   private readonly ZOOM_SENSITIVITY = 0.001 | ||||
| 
 | ||||
|   cleanups: (() => void)[] = [] | ||||
| 
 | ||||
|   constructor( | ||||
|     private container: HTMLElement, | ||||
| @ -20,19 +21,33 @@ class DiagramPanZoom { | ||||
|   ) { | ||||
|     this.setupEventListeners() | ||||
|     this.setupNavigationControls() | ||||
|     this.resetTransform() | ||||
|   } | ||||
| 
 | ||||
|   private setupEventListeners() { | ||||
|     // Mouse drag events
 | ||||
|     this.container.addEventListener("mousedown", this.onMouseDown.bind(this)) | ||||
|     document.addEventListener("mousemove", this.onMouseMove.bind(this)) | ||||
|     document.addEventListener("mouseup", this.onMouseUp.bind(this)) | ||||
|     const mouseDownHandler = this.onMouseDown.bind(this) | ||||
|     const mouseMoveHandler = this.onMouseMove.bind(this) | ||||
|     const mouseUpHandler = this.onMouseUp.bind(this) | ||||
|     const resizeHandler = this.resetTransform.bind(this) | ||||
| 
 | ||||
|     // Wheel zoom events
 | ||||
|     this.container.addEventListener("wheel", this.onWheel.bind(this), { passive: false }) | ||||
|     this.container.addEventListener("mousedown", mouseDownHandler) | ||||
|     document.addEventListener("mousemove", mouseMoveHandler) | ||||
|     document.addEventListener("mouseup", mouseUpHandler) | ||||
|     window.addEventListener("resize", resizeHandler) | ||||
| 
 | ||||
|     // Reset on window resize
 | ||||
|     window.addEventListener("resize", this.resetTransform.bind(this)) | ||||
|     this.cleanups.push( | ||||
|       () => this.container.removeEventListener("mousedown", mouseDownHandler), | ||||
|       () => document.removeEventListener("mousemove", mouseMoveHandler), | ||||
|       () => document.removeEventListener("mouseup", mouseUpHandler), | ||||
|       () => window.removeEventListener("resize", resizeHandler), | ||||
|     ) | ||||
|   } | ||||
| 
 | ||||
|   cleanup() { | ||||
|     for (const cleanup of this.cleanups) { | ||||
|       cleanup() | ||||
|     } | ||||
|   } | ||||
| 
 | ||||
|   private setupNavigationControls() { | ||||
| @ -84,26 +99,6 @@ class DiagramPanZoom { | ||||
|     this.container.style.cursor = "grab" | ||||
|   } | ||||
| 
 | ||||
|   private onWheel(e: WheelEvent) { | ||||
|     e.preventDefault() | ||||
| 
 | ||||
|     const delta = -e.deltaY * this.ZOOM_SENSITIVITY | ||||
|     const newScale = Math.min(Math.max(this.scale + delta, this.MIN_SCALE), this.MAX_SCALE) | ||||
| 
 | ||||
|     // Calculate mouse position relative to content
 | ||||
|     const rect = this.content.getBoundingClientRect() | ||||
|     const mouseX = e.clientX - rect.left | ||||
|     const mouseY = e.clientY - rect.top | ||||
| 
 | ||||
|     // Adjust pan to zoom around mouse position
 | ||||
|     const scaleDiff = newScale - this.scale | ||||
|     this.currentPan.x -= mouseX * scaleDiff | ||||
|     this.currentPan.y -= mouseY * scaleDiff | ||||
| 
 | ||||
|     this.scale = newScale | ||||
|     this.updateTransform() | ||||
|   } | ||||
| 
 | ||||
|   private zoom(delta: number) { | ||||
|     const newScale = Math.min(Math.max(this.scale + delta, this.MIN_SCALE), this.MAX_SCALE) | ||||
| 
 | ||||
| @ -126,7 +121,11 @@ class DiagramPanZoom { | ||||
| 
 | ||||
|   private resetTransform() { | ||||
|     this.scale = 1 | ||||
|     this.currentPan = { x: 0, y: 0 } | ||||
|     const svg = this.content.querySelector("svg")! | ||||
|     this.currentPan = { | ||||
|       x: svg.getBoundingClientRect().width / 2, | ||||
|       y: svg.getBoundingClientRect().height / 2, | ||||
|     } | ||||
|     this.updateTransform() | ||||
|   } | ||||
| } | ||||
| @ -149,38 +148,59 @@ document.addEventListener("nav", async () => { | ||||
|   const nodes = center.querySelectorAll("code.mermaid") as NodeListOf<HTMLElement> | ||||
|   if (nodes.length === 0) return | ||||
| 
 | ||||
|   const computedStyleMap = cssVars.reduce( | ||||
|     (acc, key) => { | ||||
|       acc[key] = getComputedStyle(document.documentElement).getPropertyValue(key) | ||||
|       return acc | ||||
|     }, | ||||
|     {} as Record<(typeof cssVars)[number], string>, | ||||
|   ) | ||||
| 
 | ||||
|   mermaidImport ||= await import( | ||||
|     // @ts-ignore
 | ||||
|     "https://cdnjs.cloudflare.com/ajax/libs/mermaid/11.4.0/mermaid.esm.min.mjs" | ||||
|   ) | ||||
|   const mermaid = mermaidImport.default | ||||
| 
 | ||||
|   const darkMode = document.documentElement.getAttribute("saved-theme") === "dark" | ||||
|   mermaid.initialize({ | ||||
|     startOnLoad: false, | ||||
|     securityLevel: "loose", | ||||
|     theme: darkMode ? "dark" : "base", | ||||
|     themeVariables: { | ||||
|       fontFamily: computedStyleMap["--codeFont"], | ||||
|       primaryColor: computedStyleMap["--light"], | ||||
|       primaryTextColor: computedStyleMap["--darkgray"], | ||||
|       primaryBorderColor: computedStyleMap["--tertiary"], | ||||
|       lineColor: computedStyleMap["--darkgray"], | ||||
|       secondaryColor: computedStyleMap["--secondary"], | ||||
|       tertiaryColor: computedStyleMap["--tertiary"], | ||||
|       clusterBkg: computedStyleMap["--light"], | ||||
|       edgeLabelBackground: computedStyleMap["--highlight"], | ||||
|     }, | ||||
|   }) | ||||
|   await mermaid.run({ nodes }) | ||||
|   const textMapping: WeakMap<HTMLElement, string> = new WeakMap() | ||||
|   for (const node of nodes) { | ||||
|     textMapping.set(node, node.innerText) | ||||
|   } | ||||
| 
 | ||||
|   async function renderMermaid() { | ||||
|     // de-init any other diagrams
 | ||||
|     for (const node of nodes) { | ||||
|       node.removeAttribute("data-processed") | ||||
|       const oldText = textMapping.get(node) | ||||
|       if (oldText) { | ||||
|         node.innerHTML = oldText | ||||
|       } | ||||
|     } | ||||
| 
 | ||||
|     const computedStyleMap = cssVars.reduce( | ||||
|       (acc, key) => { | ||||
|         acc[key] = window.getComputedStyle(document.documentElement).getPropertyValue(key) | ||||
|         return acc | ||||
|       }, | ||||
|       {} as Record<(typeof cssVars)[number], string>, | ||||
|     ) | ||||
| 
 | ||||
|     const darkMode = document.documentElement.getAttribute("saved-theme") === "dark" | ||||
|     mermaid.initialize({ | ||||
|       startOnLoad: false, | ||||
|       securityLevel: "loose", | ||||
|       theme: darkMode ? "dark" : "base", | ||||
|       themeVariables: { | ||||
|         fontFamily: computedStyleMap["--codeFont"], | ||||
|         primaryColor: computedStyleMap["--light"], | ||||
|         primaryTextColor: computedStyleMap["--darkgray"], | ||||
|         primaryBorderColor: computedStyleMap["--tertiary"], | ||||
|         lineColor: computedStyleMap["--darkgray"], | ||||
|         secondaryColor: computedStyleMap["--secondary"], | ||||
|         tertiaryColor: computedStyleMap["--tertiary"], | ||||
|         clusterBkg: computedStyleMap["--light"], | ||||
|         edgeLabelBackground: computedStyleMap["--highlight"], | ||||
|       }, | ||||
|     }) | ||||
| 
 | ||||
|     await mermaid.run({ nodes }) | ||||
|   } | ||||
| 
 | ||||
|   await renderMermaid() | ||||
|   document.addEventListener("themechange", renderMermaid) | ||||
|   window.addCleanup(() => document.removeEventListener("themechange", renderMermaid)) | ||||
| 
 | ||||
|   for (let i = 0; i < nodes.length; i++) { | ||||
|     const codeBlock = nodes[i] as HTMLElement | ||||
| @ -203,7 +223,6 @@ document.addEventListener("nav", async () => { | ||||
|     if (!popupContainer) return | ||||
| 
 | ||||
|     let panZoom: DiagramPanZoom | null = null | ||||
| 
 | ||||
|     function showMermaid() { | ||||
|       const container = popupContainer.querySelector("#mermaid-space") as HTMLElement | ||||
|       const content = popupContainer.querySelector(".mermaid-content") as HTMLElement | ||||
| @ -224,24 +243,15 @@ document.addEventListener("nav", async () => { | ||||
| 
 | ||||
|     function hideMermaid() { | ||||
|       popupContainer.classList.remove("active") | ||||
|       panZoom?.cleanup() | ||||
|       panZoom = null | ||||
|     } | ||||
| 
 | ||||
|     function handleEscape(e: any) { | ||||
|       if (e.key === "Escape") { | ||||
|         hideMermaid() | ||||
|       } | ||||
|     } | ||||
| 
 | ||||
|     const closeBtn = popupContainer.querySelector(".close-button") as HTMLButtonElement | ||||
| 
 | ||||
|     closeBtn.addEventListener("click", hideMermaid) | ||||
|     expandBtn.addEventListener("click", showMermaid) | ||||
|     registerEscapeHandler(popupContainer, hideMermaid) | ||||
|     document.addEventListener("keydown", handleEscape) | ||||
| 
 | ||||
|     window.addCleanup(() => { | ||||
|       closeBtn.removeEventListener("click", hideMermaid) | ||||
|       panZoom?.cleanup() | ||||
|       expandBtn.removeEventListener("click", showMermaid) | ||||
|     }) | ||||
|   } | ||||
|  | ||||
| @ -53,46 +53,16 @@ pre { | ||||
|   } | ||||
| 
 | ||||
|   & > #mermaid-space { | ||||
|     display: grid; | ||||
|     width: 90%; | ||||
|     height: 90vh; | ||||
|     margin: 5vh auto; | ||||
|     background: var(--light); | ||||
|     box-shadow: | ||||
|       0 14px 50px rgba(27, 33, 48, 0.12), | ||||
|       0 10px 30px rgba(27, 33, 48, 0.16); | ||||
|     border: 1px solid var(--lightgray); | ||||
|     background-color: var(--light); | ||||
|     border-radius: 5px; | ||||
|     position: fixed; | ||||
|     top: 50%; | ||||
|     left: 50%; | ||||
|     transform: translate(-50%, -50%); | ||||
|     height: 80vh; | ||||
|     width: 80vw; | ||||
|     overflow: hidden; | ||||
|     position: relative; | ||||
| 
 | ||||
|     & > .mermaid-header { | ||||
|       display: flex; | ||||
|       justify-content: flex-end; | ||||
|       padding: 1rem; | ||||
|       border-bottom: 1px solid var(--lightgray); | ||||
|       background: var(--light); | ||||
|       z-index: 2; | ||||
|       max-height: fit-content; | ||||
| 
 | ||||
|       & > .close-button { | ||||
|         display: flex; | ||||
|         align-items: center; | ||||
|         justify-content: center; | ||||
|         width: 32px; | ||||
|         height: 32px; | ||||
|         padding: 0; | ||||
|         background: transparent; | ||||
|         border: none; | ||||
|         border-radius: 4px; | ||||
|         color: var(--darkgray); | ||||
|         cursor: pointer; | ||||
|         transition: all 0.2s ease; | ||||
| 
 | ||||
|         &:hover { | ||||
|           background: var(--lightgray); | ||||
|           color: var(--dark); | ||||
|         } | ||||
|       } | ||||
|     } | ||||
| 
 | ||||
|     & > .mermaid-content { | ||||
|       padding: 2rem; | ||||
|  | ||||
| @ -675,7 +675,6 @@ export const ObsidianFlavoredMarkdown: QuartzTransformerPlugin<Partial<Options>> | ||||
|                     properties: { | ||||
|                       className: ["expand-button"], | ||||
|                       "aria-label": "Expand mermaid diagram", | ||||
|                       "aria-hidden": "true", | ||||
|                       "data-view-component": true, | ||||
|                     }, | ||||
|                     children: [ | ||||
| @ -706,70 +705,13 @@ export const ObsidianFlavoredMarkdown: QuartzTransformerPlugin<Partial<Options>> | ||||
|                   { | ||||
|                     type: "element", | ||||
|                     tagName: "div", | ||||
|                     properties: { id: "mermaid-container" }, | ||||
|                     properties: { id: "mermaid-container", role: "dialog" }, | ||||
|                     children: [ | ||||
|                       { | ||||
|                         type: "element", | ||||
|                         tagName: "div", | ||||
|                         properties: { id: "mermaid-space" }, | ||||
|                         children: [ | ||||
|                           { | ||||
|                             type: "element", | ||||
|                             tagName: "div", | ||||
|                             properties: { className: ["mermaid-header"] }, | ||||
|                             children: [ | ||||
|                               { | ||||
|                                 type: "element", | ||||
|                                 tagName: "button", | ||||
|                                 properties: { | ||||
|                                   className: ["close-button"], | ||||
|                                   "aria-label": "close button", | ||||
|                                 }, | ||||
|                                 children: [ | ||||
|                                   { | ||||
|                                     type: "element", | ||||
|                                     tagName: "svg", | ||||
|                                     properties: { | ||||
|                                       "aria-hidden": "true", | ||||
|                                       xmlns: "http://www.w3.org/2000/svg", | ||||
|                                       width: 24, | ||||
|                                       height: 24, | ||||
|                                       viewBox: "0 0 24 24", | ||||
|                                       fill: "none", | ||||
|                                       stroke: "currentColor", | ||||
|                                       "stroke-width": "2", | ||||
|                                       "stroke-linecap": "round", | ||||
|                                       "stroke-linejoin": "round", | ||||
|                                     }, | ||||
|                                     children: [ | ||||
|                                       { | ||||
|                                         type: "element", | ||||
|                                         tagName: "line", | ||||
|                                         properties: { | ||||
|                                           x1: 18, | ||||
|                                           y1: 6, | ||||
|                                           x2: 6, | ||||
|                                           y2: 18, | ||||
|                                         }, | ||||
|                                         children: [], | ||||
|                                       }, | ||||
|                                       { | ||||
|                                         type: "element", | ||||
|                                         tagName: "line", | ||||
|                                         properties: { | ||||
|                                           x1: 6, | ||||
|                                           y1: 6, | ||||
|                                           x2: 18, | ||||
|                                           y2: 18, | ||||
|                                         }, | ||||
|                                         children: [], | ||||
|                                       }, | ||||
|                                     ], | ||||
|                                   }, | ||||
|                                 ], | ||||
|                               }, | ||||
|                             ], | ||||
|                           }, | ||||
|                           { | ||||
|                             type: "element", | ||||
|                             tagName: "div", | ||||
|  | ||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user