The Trans Directory
0
fork

Configure Feed

Select the types of activity you want to include in your feed.

fix(mermaid): themechange detector + expand simplification

+87 -165
+77 -67
quartz/components/scripts/mermaid.inline.ts
··· 12 12 private scale = 1 13 13 private readonly MIN_SCALE = 0.5 14 14 private readonly MAX_SCALE = 3 15 - private readonly ZOOM_SENSITIVITY = 0.001 15 + 16 + cleanups: (() => void)[] = [] 16 17 17 18 constructor( 18 19 private container: HTMLElement, ··· 20 21 ) { 21 22 this.setupEventListeners() 22 23 this.setupNavigationControls() 24 + this.resetTransform() 23 25 } 24 26 25 27 private setupEventListeners() { 26 28 // Mouse drag events 27 - this.container.addEventListener("mousedown", this.onMouseDown.bind(this)) 28 - document.addEventListener("mousemove", this.onMouseMove.bind(this)) 29 - document.addEventListener("mouseup", this.onMouseUp.bind(this)) 29 + const mouseDownHandler = this.onMouseDown.bind(this) 30 + const mouseMoveHandler = this.onMouseMove.bind(this) 31 + const mouseUpHandler = this.onMouseUp.bind(this) 32 + const resizeHandler = this.resetTransform.bind(this) 33 + 34 + this.container.addEventListener("mousedown", mouseDownHandler) 35 + document.addEventListener("mousemove", mouseMoveHandler) 36 + document.addEventListener("mouseup", mouseUpHandler) 37 + window.addEventListener("resize", resizeHandler) 30 38 31 - // Wheel zoom events 32 - this.container.addEventListener("wheel", this.onWheel.bind(this), { passive: false }) 39 + this.cleanups.push( 40 + () => this.container.removeEventListener("mousedown", mouseDownHandler), 41 + () => document.removeEventListener("mousemove", mouseMoveHandler), 42 + () => document.removeEventListener("mouseup", mouseUpHandler), 43 + () => window.removeEventListener("resize", resizeHandler), 44 + ) 45 + } 33 46 34 - // Reset on window resize 35 - window.addEventListener("resize", this.resetTransform.bind(this)) 47 + cleanup() { 48 + for (const cleanup of this.cleanups) { 49 + cleanup() 50 + } 36 51 } 37 52 38 53 private setupNavigationControls() { ··· 84 99 this.container.style.cursor = "grab" 85 100 } 86 101 87 - private onWheel(e: WheelEvent) { 88 - e.preventDefault() 89 - 90 - const delta = -e.deltaY * this.ZOOM_SENSITIVITY 91 - const newScale = Math.min(Math.max(this.scale + delta, this.MIN_SCALE), this.MAX_SCALE) 92 - 93 - // Calculate mouse position relative to content 94 - const rect = this.content.getBoundingClientRect() 95 - const mouseX = e.clientX - rect.left 96 - const mouseY = e.clientY - rect.top 97 - 98 - // Adjust pan to zoom around mouse position 99 - const scaleDiff = newScale - this.scale 100 - this.currentPan.x -= mouseX * scaleDiff 101 - this.currentPan.y -= mouseY * scaleDiff 102 - 103 - this.scale = newScale 104 - this.updateTransform() 105 - } 106 - 107 102 private zoom(delta: number) { 108 103 const newScale = Math.min(Math.max(this.scale + delta, this.MIN_SCALE), this.MAX_SCALE) 109 104 ··· 126 121 127 122 private resetTransform() { 128 123 this.scale = 1 129 - this.currentPan = { x: 0, y: 0 } 124 + const svg = this.content.querySelector("svg")! 125 + this.currentPan = { 126 + x: svg.getBoundingClientRect().width / 2, 127 + y: svg.getBoundingClientRect().height / 2, 128 + } 130 129 this.updateTransform() 131 130 } 132 131 } ··· 149 148 const nodes = center.querySelectorAll("code.mermaid") as NodeListOf<HTMLElement> 150 149 if (nodes.length === 0) return 151 150 152 - const computedStyleMap = cssVars.reduce( 153 - (acc, key) => { 154 - acc[key] = getComputedStyle(document.documentElement).getPropertyValue(key) 155 - return acc 156 - }, 157 - {} as Record<(typeof cssVars)[number], string>, 158 - ) 159 - 160 151 mermaidImport ||= await import( 161 152 // @ts-ignore 162 153 "https://cdnjs.cloudflare.com/ajax/libs/mermaid/11.4.0/mermaid.esm.min.mjs" 163 154 ) 164 155 const mermaid = mermaidImport.default 165 156 166 - const darkMode = document.documentElement.getAttribute("saved-theme") === "dark" 167 - mermaid.initialize({ 168 - startOnLoad: false, 169 - securityLevel: "loose", 170 - theme: darkMode ? "dark" : "base", 171 - themeVariables: { 172 - fontFamily: computedStyleMap["--codeFont"], 173 - primaryColor: computedStyleMap["--light"], 174 - primaryTextColor: computedStyleMap["--darkgray"], 175 - primaryBorderColor: computedStyleMap["--tertiary"], 176 - lineColor: computedStyleMap["--darkgray"], 177 - secondaryColor: computedStyleMap["--secondary"], 178 - tertiaryColor: computedStyleMap["--tertiary"], 179 - clusterBkg: computedStyleMap["--light"], 180 - edgeLabelBackground: computedStyleMap["--highlight"], 181 - }, 182 - }) 183 - await mermaid.run({ nodes }) 157 + const textMapping: WeakMap<HTMLElement, string> = new WeakMap() 158 + for (const node of nodes) { 159 + textMapping.set(node, node.innerText) 160 + } 161 + 162 + async function renderMermaid() { 163 + // de-init any other diagrams 164 + for (const node of nodes) { 165 + node.removeAttribute("data-processed") 166 + const oldText = textMapping.get(node) 167 + if (oldText) { 168 + node.innerHTML = oldText 169 + } 170 + } 171 + 172 + const computedStyleMap = cssVars.reduce( 173 + (acc, key) => { 174 + acc[key] = window.getComputedStyle(document.documentElement).getPropertyValue(key) 175 + return acc 176 + }, 177 + {} as Record<(typeof cssVars)[number], string>, 178 + ) 179 + 180 + const darkMode = document.documentElement.getAttribute("saved-theme") === "dark" 181 + mermaid.initialize({ 182 + startOnLoad: false, 183 + securityLevel: "loose", 184 + theme: darkMode ? "dark" : "base", 185 + themeVariables: { 186 + fontFamily: computedStyleMap["--codeFont"], 187 + primaryColor: computedStyleMap["--light"], 188 + primaryTextColor: computedStyleMap["--darkgray"], 189 + primaryBorderColor: computedStyleMap["--tertiary"], 190 + lineColor: computedStyleMap["--darkgray"], 191 + secondaryColor: computedStyleMap["--secondary"], 192 + tertiaryColor: computedStyleMap["--tertiary"], 193 + clusterBkg: computedStyleMap["--light"], 194 + edgeLabelBackground: computedStyleMap["--highlight"], 195 + }, 196 + }) 197 + 198 + await mermaid.run({ nodes }) 199 + } 200 + 201 + await renderMermaid() 202 + document.addEventListener("themechange", renderMermaid) 203 + window.addCleanup(() => document.removeEventListener("themechange", renderMermaid)) 184 204 185 205 for (let i = 0; i < nodes.length; i++) { 186 206 const codeBlock = nodes[i] as HTMLElement ··· 203 223 if (!popupContainer) return 204 224 205 225 let panZoom: DiagramPanZoom | null = null 206 - 207 226 function showMermaid() { 208 227 const container = popupContainer.querySelector("#mermaid-space") as HTMLElement 209 228 const content = popupContainer.querySelector(".mermaid-content") as HTMLElement ··· 224 243 225 244 function hideMermaid() { 226 245 popupContainer.classList.remove("active") 246 + panZoom?.cleanup() 227 247 panZoom = null 228 248 } 229 249 230 - function handleEscape(e: any) { 231 - if (e.key === "Escape") { 232 - hideMermaid() 233 - } 234 - } 235 - 236 - const closeBtn = popupContainer.querySelector(".close-button") as HTMLButtonElement 237 - 238 - closeBtn.addEventListener("click", hideMermaid) 239 250 expandBtn.addEventListener("click", showMermaid) 240 251 registerEscapeHandler(popupContainer, hideMermaid) 241 - document.addEventListener("keydown", handleEscape) 242 252 243 253 window.addCleanup(() => { 244 - closeBtn.removeEventListener("click", hideMermaid) 254 + panZoom?.cleanup() 245 255 expandBtn.removeEventListener("click", showMermaid) 246 256 }) 247 257 }
+9 -39
quartz/components/styles/mermaid.inline.scss
··· 53 53 } 54 54 55 55 & > #mermaid-space { 56 - display: grid; 57 - width: 90%; 58 - height: 90vh; 59 - margin: 5vh auto; 60 - background: var(--light); 61 - box-shadow: 62 - 0 14px 50px rgba(27, 33, 48, 0.12), 63 - 0 10px 30px rgba(27, 33, 48, 0.16); 56 + border: 1px solid var(--lightgray); 57 + background-color: var(--light); 58 + border-radius: 5px; 59 + position: fixed; 60 + top: 50%; 61 + left: 50%; 62 + transform: translate(-50%, -50%); 63 + height: 80vh; 64 + width: 80vw; 64 65 overflow: hidden; 65 - position: relative; 66 - 67 - & > .mermaid-header { 68 - display: flex; 69 - justify-content: flex-end; 70 - padding: 1rem; 71 - border-bottom: 1px solid var(--lightgray); 72 - background: var(--light); 73 - z-index: 2; 74 - max-height: fit-content; 75 - 76 - & > .close-button { 77 - display: flex; 78 - align-items: center; 79 - justify-content: center; 80 - width: 32px; 81 - height: 32px; 82 - padding: 0; 83 - background: transparent; 84 - border: none; 85 - border-radius: 4px; 86 - color: var(--darkgray); 87 - cursor: pointer; 88 - transition: all 0.2s ease; 89 - 90 - &:hover { 91 - background: var(--lightgray); 92 - color: var(--dark); 93 - } 94 - } 95 - } 96 66 97 67 & > .mermaid-content { 98 68 padding: 2rem;
+1 -59
quartz/plugins/transformers/ofm.ts
··· 675 675 properties: { 676 676 className: ["expand-button"], 677 677 "aria-label": "Expand mermaid diagram", 678 - "aria-hidden": "true", 679 678 "data-view-component": true, 680 679 }, 681 680 children: [ ··· 706 705 { 707 706 type: "element", 708 707 tagName: "div", 709 - properties: { id: "mermaid-container" }, 708 + properties: { id: "mermaid-container", role: "dialog" }, 710 709 children: [ 711 710 { 712 711 type: "element", 713 712 tagName: "div", 714 713 properties: { id: "mermaid-space" }, 715 714 children: [ 716 - { 717 - type: "element", 718 - tagName: "div", 719 - properties: { className: ["mermaid-header"] }, 720 - children: [ 721 - { 722 - type: "element", 723 - tagName: "button", 724 - properties: { 725 - className: ["close-button"], 726 - "aria-label": "close button", 727 - }, 728 - children: [ 729 - { 730 - type: "element", 731 - tagName: "svg", 732 - properties: { 733 - "aria-hidden": "true", 734 - xmlns: "http://www.w3.org/2000/svg", 735 - width: 24, 736 - height: 24, 737 - viewBox: "0 0 24 24", 738 - fill: "none", 739 - stroke: "currentColor", 740 - "stroke-width": "2", 741 - "stroke-linecap": "round", 742 - "stroke-linejoin": "round", 743 - }, 744 - children: [ 745 - { 746 - type: "element", 747 - tagName: "line", 748 - properties: { 749 - x1: 18, 750 - y1: 6, 751 - x2: 6, 752 - y2: 18, 753 - }, 754 - children: [], 755 - }, 756 - { 757 - type: "element", 758 - tagName: "line", 759 - properties: { 760 - x1: 6, 761 - y1: 6, 762 - x2: 18, 763 - y2: 18, 764 - }, 765 - children: [], 766 - }, 767 - ], 768 - }, 769 - ], 770 - }, 771 - ], 772 - }, 773 715 { 774 716 type: "element", 775 717 tagName: "div",