Aaron Pham
2024-11-10 1ab9c91df1df799f7a1004200ff890b144d00f19
feat(mermaid): improvement navigation (#1575)

* feat(mermaid): custom stuff

Signed-off-by: Aaron Pham <contact@aarnphm.xyz>

* chore: use mermaid and update clipboard content

Signed-off-by: Aaron Pham <contact@aarnphm.xyz>

* fix: explicitly use center div

Signed-off-by: Aaron Pham <contact@aarnphm.xyz>

---------

Signed-off-by: Aaron Pham <contact@aarnphm.xyz>
2 files added
4 files modified
1202 ■■■■■ changed files
package-lock.json 631 ●●●●● patch | view | raw | blame | history
package.json 1 ●●●● patch | view | raw | blame | history
quartz/components/scripts/clipboard.inline.ts 4 ●●● patch | view | raw | blame | history
quartz/components/scripts/mermaid.inline.ts 242 ●●●●● patch | view | raw | blame | history
quartz/components/styles/mermaid.inline.scss 163 ●●●●● patch | view | raw | blame | history
quartz/plugins/transformers/ofm.ts 161 ●●●● patch | view | raw | blame | history
package-lock.json
@@ -32,6 +32,7 @@
        "mdast-util-find-and-replace": "^3.0.1",
        "mdast-util-to-hast": "^13.2.0",
        "mdast-util-to-string": "^4.0.0",
        "mermaid": "^11.4.0",
        "micromorph": "^0.4.5",
        "pixi.js": "^8.5.2",
        "preact": "^10.24.3",
@@ -91,6 +92,28 @@
        "npm": ">=9.3.1"
      }
    },
    "node_modules/@antfu/install-pkg": {
      "version": "0.4.1",
      "resolved": "https://registry.npmjs.org/@antfu/install-pkg/-/install-pkg-0.4.1.tgz",
      "integrity": "sha512-T7yB5QNG29afhWVkVq7XeIMBa5U/vs9mX69YqayXypPRmYzUmzwnYltplHmPtZ4HPCn+sQKeXW8I47wCbuBOjw==",
      "license": "MIT",
      "dependencies": {
        "package-manager-detector": "^0.2.0",
        "tinyexec": "^0.3.0"
      },
      "funding": {
        "url": "https://github.com/sponsors/antfu"
      }
    },
    "node_modules/@antfu/utils": {
      "version": "0.7.10",
      "resolved": "https://registry.npmjs.org/@antfu/utils/-/utils-0.7.10.tgz",
      "integrity": "sha512-+562v9k4aI80m1+VuMHehNJWLOFjBnXn3tdOitzD0il5b7smkSBal4+a3oKiQTbrwMmN/TBUMDvbdoWDehgOww==",
      "license": "MIT",
      "funding": {
        "url": "https://github.com/sponsors/antfu"
      }
    },
    "node_modules/@asamuzakjp/dom-selector": {
      "version": "2.0.2",
      "resolved": "https://registry.npmjs.org/@asamuzakjp/dom-selector/-/dom-selector-2.0.2.tgz",
@@ -101,12 +124,57 @@
        "is-potential-custom-element-name": "^1.0.1"
      }
    },
    "node_modules/@braintree/sanitize-url": {
      "version": "7.1.0",
      "resolved": "https://registry.npmjs.org/@braintree/sanitize-url/-/sanitize-url-7.1.0.tgz",
      "integrity": "sha512-o+UlMLt49RvtCASlOMW0AkHnabN9wR9rwCCherxO0yG4Npy34GkvrAqdXQvrhNs+jh+gkK8gB8Lf05qL/O7KWg==",
      "license": "MIT"
    },
    "node_modules/@bufbuild/protobuf": {
      "version": "2.2.0",
      "resolved": "https://registry.npmjs.org/@bufbuild/protobuf/-/protobuf-2.2.0.tgz",
      "integrity": "sha512-+imAQkHf7U/Rwvu0wk1XWgsP3WnpCWmK7B48f0XqSNzgk64+grljTKC7pnO/xBiEMUziF7vKRfbBnOQhg126qQ==",
      "peer": true
    },
    "node_modules/@chevrotain/cst-dts-gen": {
      "version": "11.0.3",
      "resolved": "https://registry.npmjs.org/@chevrotain/cst-dts-gen/-/cst-dts-gen-11.0.3.tgz",
      "integrity": "sha512-BvIKpRLeS/8UbfxXxgC33xOumsacaeCKAjAeLyOn7Pcp95HiRbrpl14S+9vaZLolnbssPIUuiUd8IvgkRyt6NQ==",
      "license": "Apache-2.0",
      "dependencies": {
        "@chevrotain/gast": "11.0.3",
        "@chevrotain/types": "11.0.3",
        "lodash-es": "4.17.21"
      }
    },
    "node_modules/@chevrotain/gast": {
      "version": "11.0.3",
      "resolved": "https://registry.npmjs.org/@chevrotain/gast/-/gast-11.0.3.tgz",
      "integrity": "sha512-+qNfcoNk70PyS/uxmj3li5NiECO+2YKZZQMbmjTqRI3Qchu8Hig/Q9vgkHpI3alNjr7M+a2St5pw5w5F6NL5/Q==",
      "license": "Apache-2.0",
      "dependencies": {
        "@chevrotain/types": "11.0.3",
        "lodash-es": "4.17.21"
      }
    },
    "node_modules/@chevrotain/regexp-to-ast": {
      "version": "11.0.3",
      "resolved": "https://registry.npmjs.org/@chevrotain/regexp-to-ast/-/regexp-to-ast-11.0.3.tgz",
      "integrity": "sha512-1fMHaBZxLFvWI067AVbGJav1eRY7N8DDvYCTwGBiE/ytKBgP8azTdgyrKyWZ9Mfh09eHWb5PgTSO8wi7U824RA==",
      "license": "Apache-2.0"
    },
    "node_modules/@chevrotain/types": {
      "version": "11.0.3",
      "resolved": "https://registry.npmjs.org/@chevrotain/types/-/types-11.0.3.tgz",
      "integrity": "sha512-gsiM3G8b58kZC2HaWR50gu6Y1440cHiJ+i3JUvcp/35JchYejb2+5MVeJK0iKThYpAa/P2PYFV4hoi44HD+aHQ==",
      "license": "Apache-2.0"
    },
    "node_modules/@chevrotain/utils": {
      "version": "11.0.3",
      "resolved": "https://registry.npmjs.org/@chevrotain/utils/-/utils-11.0.3.tgz",
      "integrity": "sha512-YslZMgtJUyuMbZ+aKvfF3x1f5liK4mWNxghFRv7jqRR9C3R3fAOGTTKvxXDa2Y1s9zSbcpuO0cAxDYsc9SrXoQ==",
      "license": "Apache-2.0"
    },
    "node_modules/@citation-js/core": {
      "version": "0.7.14",
      "resolved": "https://registry.npmjs.org/@citation-js/core/-/core-0.7.14.tgz",
@@ -600,6 +668,27 @@
      "resolved": "https://registry.npmjs.org/@floating-ui/utils/-/utils-0.2.8.tgz",
      "integrity": "sha512-kym7SodPp8/wloecOpcmSnWJsK7M0E5Wg8UcFA+uO4B9s5d0ywXOEro/8HM9x0rW+TljRzul/14UYz3TleT3ig=="
    },
    "node_modules/@iconify/types": {
      "version": "2.0.0",
      "resolved": "https://registry.npmjs.org/@iconify/types/-/types-2.0.0.tgz",
      "integrity": "sha512-+wluvCrRhXrhyOmRDJ3q8mux9JkKy5SJ/v8ol2tu4FVjyYvtEzkc/3pK15ET6RKg4b4w4BmTk1+gsCUhf21Ykg==",
      "license": "MIT"
    },
    "node_modules/@iconify/utils": {
      "version": "2.1.33",
      "resolved": "https://registry.npmjs.org/@iconify/utils/-/utils-2.1.33.tgz",
      "integrity": "sha512-jP9h6v/g0BIZx0p7XGJJVtkVnydtbgTgt9mVNcGDYwaa7UhdHdI9dvoq+gKj9sijMSJKxUPEG2JyjsgXjxL7Kw==",
      "license": "MIT",
      "dependencies": {
        "@antfu/install-pkg": "^0.4.0",
        "@antfu/utils": "^0.7.10",
        "@iconify/types": "^2.0.0",
        "debug": "^4.3.6",
        "kolorist": "^1.8.0",
        "local-pkg": "^0.5.0",
        "mlly": "^1.7.1"
      }
    },
    "node_modules/@isaacs/cliui": {
      "version": "8.0.2",
      "resolved": "https://registry.npmjs.org/@isaacs/cliui/-/cliui-8.0.2.tgz",
@@ -616,6 +705,15 @@
        "node": ">=12"
      }
    },
    "node_modules/@mermaid-js/parser": {
      "version": "0.3.0",
      "resolved": "https://registry.npmjs.org/@mermaid-js/parser/-/parser-0.3.0.tgz",
      "integrity": "sha512-HsvL6zgE5sUPGgkIDlmAWR1HTNHz2Iy11BAWPTa4Jjabkpguy4Ze2gzfLrg6pdRuBvFwgUYyxiaNqZwrEEXepA==",
      "license": "MIT",
      "dependencies": {
        "langium": "3.0.0"
      }
    },
    "node_modules/@napi-rs/simple-git": {
      "version": "0.1.19",
      "resolved": "https://registry.npmjs.org/@napi-rs/simple-git/-/simple-git-0.1.19.tgz",
@@ -979,7 +1077,6 @@
      "version": "7.4.3",
      "resolved": "https://registry.npmjs.org/@types/d3/-/d3-7.4.3.tgz",
      "integrity": "sha512-lZXZ9ckh5R8uiFVt8ogUNf+pIrK4EsWrx2Np75WvF/eTpJ0FMHNhjXk8CKEx/+gpHbNQyJWehbFaTvqmHWB3ww==",
      "dev": true,
      "dependencies": {
        "@types/d3-array": "*",
        "@types/d3-axis": "*",
@@ -1016,14 +1113,12 @@
    "node_modules/@types/d3-array": {
      "version": "3.0.5",
      "resolved": "https://registry.npmjs.org/@types/d3-array/-/d3-array-3.0.5.tgz",
      "integrity": "sha512-Qk7fpJ6qFp+26VeQ47WY0mkwXaiq8+76RJcncDEfMc2ocRzXLO67bLFRNI4OX1aGBoPzsM5Y2T+/m1pldOgD+A==",
      "dev": true
      "integrity": "sha512-Qk7fpJ6qFp+26VeQ47WY0mkwXaiq8+76RJcncDEfMc2ocRzXLO67bLFRNI4OX1aGBoPzsM5Y2T+/m1pldOgD+A=="
    },
    "node_modules/@types/d3-axis": {
      "version": "3.0.2",
      "resolved": "https://registry.npmjs.org/@types/d3-axis/-/d3-axis-3.0.2.tgz",
      "integrity": "sha512-uGC7DBh0TZrU/LY43Fd8Qr+2ja1FKmH07q2FoZFHo1eYl8aj87GhfVoY1saJVJiq24rp1+wpI6BvQJMKgQm8oA==",
      "dev": true,
      "dependencies": {
        "@types/d3-selection": "*"
      }
@@ -1032,7 +1127,6 @@
      "version": "3.0.2",
      "resolved": "https://registry.npmjs.org/@types/d3-brush/-/d3-brush-3.0.2.tgz",
      "integrity": "sha512-2TEm8KzUG3N7z0TrSKPmbxByBx54M+S9lHoP2J55QuLU0VSQ9mE96EJSAOVNEqd1bbynMjeTS9VHmz8/bSw8rA==",
      "dev": true,
      "dependencies": {
        "@types/d3-selection": "*"
      }
@@ -1040,20 +1134,17 @@
    "node_modules/@types/d3-chord": {
      "version": "3.0.2",
      "resolved": "https://registry.npmjs.org/@types/d3-chord/-/d3-chord-3.0.2.tgz",
      "integrity": "sha512-abT/iLHD3sGZwqMTX1TYCMEulr+wBd0SzyOQnjYNLp7sngdOHYtNkMRI5v3w5thoN+BWtlHVDx2Osvq6fxhZWw==",
      "dev": true
      "integrity": "sha512-abT/iLHD3sGZwqMTX1TYCMEulr+wBd0SzyOQnjYNLp7sngdOHYtNkMRI5v3w5thoN+BWtlHVDx2Osvq6fxhZWw=="
    },
    "node_modules/@types/d3-color": {
      "version": "3.1.0",
      "resolved": "https://registry.npmjs.org/@types/d3-color/-/d3-color-3.1.0.tgz",
      "integrity": "sha512-HKuicPHJuvPgCD+np6Se9MQvS6OCbJmOjGvylzMJRlDwUXjKTTXs6Pwgk79O09Vj/ho3u1ofXnhFOaEWWPrlwA==",
      "dev": true
      "integrity": "sha512-HKuicPHJuvPgCD+np6Se9MQvS6OCbJmOjGvylzMJRlDwUXjKTTXs6Pwgk79O09Vj/ho3u1ofXnhFOaEWWPrlwA=="
    },
    "node_modules/@types/d3-contour": {
      "version": "3.0.2",
      "resolved": "https://registry.npmjs.org/@types/d3-contour/-/d3-contour-3.0.2.tgz",
      "integrity": "sha512-k6/bGDoAGJZnZWaKzeB+9glgXCYGvh6YlluxzBREiVo8f/X2vpTEdgPy9DN7Z2i42PZOZ4JDhVdlTSTSkLDPlQ==",
      "dev": true,
      "dependencies": {
        "@types/d3-array": "*",
        "@types/geojson": "*"
@@ -1062,20 +1153,17 @@
    "node_modules/@types/d3-delaunay": {
      "version": "6.0.1",
      "resolved": "https://registry.npmjs.org/@types/d3-delaunay/-/d3-delaunay-6.0.1.tgz",
      "integrity": "sha512-tLxQ2sfT0p6sxdG75c6f/ekqxjyYR0+LwPrsO1mbC9YDBzPJhs2HbJJRrn8Ez1DBoHRo2yx7YEATI+8V1nGMnQ==",
      "dev": true
      "integrity": "sha512-tLxQ2sfT0p6sxdG75c6f/ekqxjyYR0+LwPrsO1mbC9YDBzPJhs2HbJJRrn8Ez1DBoHRo2yx7YEATI+8V1nGMnQ=="
    },
    "node_modules/@types/d3-dispatch": {
      "version": "3.0.2",
      "resolved": "https://registry.npmjs.org/@types/d3-dispatch/-/d3-dispatch-3.0.2.tgz",
      "integrity": "sha512-rxN6sHUXEZYCKV05MEh4z4WpPSqIw+aP7n9ZN6WYAAvZoEAghEK1WeVZMZcHRBwyaKflU43PCUAJNjFxCzPDjg==",
      "dev": true
      "integrity": "sha512-rxN6sHUXEZYCKV05MEh4z4WpPSqIw+aP7n9ZN6WYAAvZoEAghEK1WeVZMZcHRBwyaKflU43PCUAJNjFxCzPDjg=="
    },
    "node_modules/@types/d3-drag": {
      "version": "3.0.2",
      "resolved": "https://registry.npmjs.org/@types/d3-drag/-/d3-drag-3.0.2.tgz",
      "integrity": "sha512-qmODKEDvyKWVHcWWCOVcuVcOwikLVsyc4q4EBJMREsoQnR2Qoc2cZQUyFUPgO9q4S3qdSqJKBsuefv+h0Qy+tw==",
      "dev": true,
      "dependencies": {
        "@types/d3-selection": "*"
      }
@@ -1083,20 +1171,17 @@
    "node_modules/@types/d3-dsv": {
      "version": "3.0.1",
      "resolved": "https://registry.npmjs.org/@types/d3-dsv/-/d3-dsv-3.0.1.tgz",
      "integrity": "sha512-76pBHCMTvPLt44wFOieouXcGXWOF0AJCceUvaFkxSZEu4VDUdv93JfpMa6VGNFs01FHfuP4a5Ou68eRG1KBfTw==",
      "dev": true
      "integrity": "sha512-76pBHCMTvPLt44wFOieouXcGXWOF0AJCceUvaFkxSZEu4VDUdv93JfpMa6VGNFs01FHfuP4a5Ou68eRG1KBfTw=="
    },
    "node_modules/@types/d3-ease": {
      "version": "3.0.0",
      "resolved": "https://registry.npmjs.org/@types/d3-ease/-/d3-ease-3.0.0.tgz",
      "integrity": "sha512-aMo4eaAOijJjA6uU+GIeW018dvy9+oH5Y2VPPzjjfxevvGQ/oRDs+tfYC9b50Q4BygRR8yE2QCLsrT0WtAVseA==",
      "dev": true
      "integrity": "sha512-aMo4eaAOijJjA6uU+GIeW018dvy9+oH5Y2VPPzjjfxevvGQ/oRDs+tfYC9b50Q4BygRR8yE2QCLsrT0WtAVseA=="
    },
    "node_modules/@types/d3-fetch": {
      "version": "3.0.2",
      "resolved": "https://registry.npmjs.org/@types/d3-fetch/-/d3-fetch-3.0.2.tgz",
      "integrity": "sha512-gllwYWozWfbep16N9fByNBDTkJW/SyhH6SGRlXloR7WdtAaBui4plTP+gbUgiEot7vGw/ZZop1yDZlgXXSuzjA==",
      "dev": true,
      "dependencies": {
        "@types/d3-dsv": "*"
      }
@@ -1104,20 +1189,17 @@
    "node_modules/@types/d3-force": {
      "version": "3.0.4",
      "resolved": "https://registry.npmjs.org/@types/d3-force/-/d3-force-3.0.4.tgz",
      "integrity": "sha512-q7xbVLrWcXvSBBEoadowIUJ7sRpS1yvgMWnzHJggFy5cUZBq2HZL5k/pBSm0GdYWS1vs5/EDwMjSKF55PDY4Aw==",
      "dev": true
      "integrity": "sha512-q7xbVLrWcXvSBBEoadowIUJ7sRpS1yvgMWnzHJggFy5cUZBq2HZL5k/pBSm0GdYWS1vs5/EDwMjSKF55PDY4Aw=="
    },
    "node_modules/@types/d3-format": {
      "version": "3.0.1",
      "resolved": "https://registry.npmjs.org/@types/d3-format/-/d3-format-3.0.1.tgz",
      "integrity": "sha512-5KY70ifCCzorkLuIkDe0Z9YTf9RR2CjBX1iaJG+rgM/cPP+sO+q9YdQ9WdhQcgPj1EQiJ2/0+yUkkziTG6Lubg==",
      "dev": true
      "integrity": "sha512-5KY70ifCCzorkLuIkDe0Z9YTf9RR2CjBX1iaJG+rgM/cPP+sO+q9YdQ9WdhQcgPj1EQiJ2/0+yUkkziTG6Lubg=="
    },
    "node_modules/@types/d3-geo": {
      "version": "3.0.3",
      "resolved": "https://registry.npmjs.org/@types/d3-geo/-/d3-geo-3.0.3.tgz",
      "integrity": "sha512-bK9uZJS3vuDCNeeXQ4z3u0E7OeJZXjUgzFdSOtNtMCJCLvDtWDwfpRVWlyt3y8EvRzI0ccOu9xlMVirawolSCw==",
      "dev": true,
      "dependencies": {
        "@types/geojson": "*"
      }
@@ -1125,14 +1207,12 @@
    "node_modules/@types/d3-hierarchy": {
      "version": "3.1.2",
      "resolved": "https://registry.npmjs.org/@types/d3-hierarchy/-/d3-hierarchy-3.1.2.tgz",
      "integrity": "sha512-9hjRTVoZjRFR6xo8igAJyNXQyPX6Aq++Nhb5ebrUF414dv4jr2MitM2fWiOY475wa3Za7TOS2Gh9fmqEhLTt0A==",
      "dev": true
      "integrity": "sha512-9hjRTVoZjRFR6xo8igAJyNXQyPX6Aq++Nhb5ebrUF414dv4jr2MitM2fWiOY475wa3Za7TOS2Gh9fmqEhLTt0A=="
    },
    "node_modules/@types/d3-interpolate": {
      "version": "3.0.1",
      "resolved": "https://registry.npmjs.org/@types/d3-interpolate/-/d3-interpolate-3.0.1.tgz",
      "integrity": "sha512-jx5leotSeac3jr0RePOH1KdR9rISG91QIE4Q2PYTu4OymLTZfA3SrnURSLzKH48HmXVUru50b8nje4E79oQSQw==",
      "dev": true,
      "dependencies": {
        "@types/d3-color": "*"
      }
@@ -1140,32 +1220,27 @@
    "node_modules/@types/d3-path": {
      "version": "3.0.0",
      "resolved": "https://registry.npmjs.org/@types/d3-path/-/d3-path-3.0.0.tgz",
      "integrity": "sha512-0g/A+mZXgFkQxN3HniRDbXMN79K3CdTpLsevj+PXiTcb2hVyvkZUBg37StmgCQkaD84cUJ4uaDAWq7UJOQy2Tg==",
      "dev": true
      "integrity": "sha512-0g/A+mZXgFkQxN3HniRDbXMN79K3CdTpLsevj+PXiTcb2hVyvkZUBg37StmgCQkaD84cUJ4uaDAWq7UJOQy2Tg=="
    },
    "node_modules/@types/d3-polygon": {
      "version": "3.0.0",
      "resolved": "https://registry.npmjs.org/@types/d3-polygon/-/d3-polygon-3.0.0.tgz",
      "integrity": "sha512-D49z4DyzTKXM0sGKVqiTDTYr+DHg/uxsiWDAkNrwXYuiZVd9o9wXZIo+YsHkifOiyBkmSWlEngHCQme54/hnHw==",
      "dev": true
      "integrity": "sha512-D49z4DyzTKXM0sGKVqiTDTYr+DHg/uxsiWDAkNrwXYuiZVd9o9wXZIo+YsHkifOiyBkmSWlEngHCQme54/hnHw=="
    },
    "node_modules/@types/d3-quadtree": {
      "version": "3.0.2",
      "resolved": "https://registry.npmjs.org/@types/d3-quadtree/-/d3-quadtree-3.0.2.tgz",
      "integrity": "sha512-QNcK8Jguvc8lU+4OfeNx+qnVy7c0VrDJ+CCVFS9srBo2GL9Y18CnIxBdTF3v38flrGy5s1YggcoAiu6s4fLQIw==",
      "dev": true
      "integrity": "sha512-QNcK8Jguvc8lU+4OfeNx+qnVy7c0VrDJ+CCVFS9srBo2GL9Y18CnIxBdTF3v38flrGy5s1YggcoAiu6s4fLQIw=="
    },
    "node_modules/@types/d3-random": {
      "version": "3.0.1",
      "resolved": "https://registry.npmjs.org/@types/d3-random/-/d3-random-3.0.1.tgz",
      "integrity": "sha512-IIE6YTekGczpLYo/HehAy3JGF1ty7+usI97LqraNa8IiDur+L44d0VOjAvFQWJVdZOJHukUJw+ZdZBlgeUsHOQ==",
      "dev": true
      "integrity": "sha512-IIE6YTekGczpLYo/HehAy3JGF1ty7+usI97LqraNa8IiDur+L44d0VOjAvFQWJVdZOJHukUJw+ZdZBlgeUsHOQ=="
    },
    "node_modules/@types/d3-scale": {
      "version": "4.0.3",
      "resolved": "https://registry.npmjs.org/@types/d3-scale/-/d3-scale-4.0.3.tgz",
      "integrity": "sha512-PATBiMCpvHJSMtZAMEhc2WyL+hnzarKzI6wAHYjhsonjWJYGq5BXTzQjv4l8m2jO183/4wZ90rKvSeT7o72xNQ==",
      "dev": true,
      "dependencies": {
        "@types/d3-time": "*"
      }
@@ -1173,20 +1248,17 @@
    "node_modules/@types/d3-scale-chromatic": {
      "version": "3.0.0",
      "resolved": "https://registry.npmjs.org/@types/d3-scale-chromatic/-/d3-scale-chromatic-3.0.0.tgz",
      "integrity": "sha512-dsoJGEIShosKVRBZB0Vo3C8nqSDqVGujJU6tPznsBJxNJNwMF8utmS83nvCBKQYPpjCzaaHcrf66iTRpZosLPw==",
      "dev": true
      "integrity": "sha512-dsoJGEIShosKVRBZB0Vo3C8nqSDqVGujJU6tPznsBJxNJNwMF8utmS83nvCBKQYPpjCzaaHcrf66iTRpZosLPw=="
    },
    "node_modules/@types/d3-selection": {
      "version": "3.0.5",
      "resolved": "https://registry.npmjs.org/@types/d3-selection/-/d3-selection-3.0.5.tgz",
      "integrity": "sha512-xCB0z3Hi8eFIqyja3vW8iV01+OHGYR2di/+e+AiOcXIOrY82lcvWW8Ke1DYE/EUVMsBl4Db9RppSBS3X1U6J0w==",
      "dev": true
      "integrity": "sha512-xCB0z3Hi8eFIqyja3vW8iV01+OHGYR2di/+e+AiOcXIOrY82lcvWW8Ke1DYE/EUVMsBl4Db9RppSBS3X1U6J0w=="
    },
    "node_modules/@types/d3-shape": {
      "version": "3.1.1",
      "resolved": "https://registry.npmjs.org/@types/d3-shape/-/d3-shape-3.1.1.tgz",
      "integrity": "sha512-6Uh86YFF7LGg4PQkuO2oG6EMBRLuW9cbavUW46zkIO5kuS2PfTqo2o9SkgtQzguBHbLgNnU90UNsITpsX1My+A==",
      "dev": true,
      "dependencies": {
        "@types/d3-path": "*"
      }
@@ -1194,26 +1266,22 @@
    "node_modules/@types/d3-time": {
      "version": "3.0.0",
      "resolved": "https://registry.npmjs.org/@types/d3-time/-/d3-time-3.0.0.tgz",
      "integrity": "sha512-sZLCdHvBUcNby1cB6Fd3ZBrABbjz3v1Vm90nysCQ6Vt7vd6e/h9Lt7SiJUoEX0l4Dzc7P5llKyhqSi1ycSf1Hg==",
      "dev": true
      "integrity": "sha512-sZLCdHvBUcNby1cB6Fd3ZBrABbjz3v1Vm90nysCQ6Vt7vd6e/h9Lt7SiJUoEX0l4Dzc7P5llKyhqSi1ycSf1Hg=="
    },
    "node_modules/@types/d3-time-format": {
      "version": "4.0.0",
      "resolved": "https://registry.npmjs.org/@types/d3-time-format/-/d3-time-format-4.0.0.tgz",
      "integrity": "sha512-yjfBUe6DJBsDin2BMIulhSHmr5qNR5Pxs17+oW4DoVPyVIXZ+m6bs7j1UVKP08Emv6jRmYrYqxYzO63mQxy1rw==",
      "dev": true
      "integrity": "sha512-yjfBUe6DJBsDin2BMIulhSHmr5qNR5Pxs17+oW4DoVPyVIXZ+m6bs7j1UVKP08Emv6jRmYrYqxYzO63mQxy1rw=="
    },
    "node_modules/@types/d3-timer": {
      "version": "3.0.0",
      "resolved": "https://registry.npmjs.org/@types/d3-timer/-/d3-timer-3.0.0.tgz",
      "integrity": "sha512-HNB/9GHqu7Fo8AQiugyJbv6ZxYz58wef0esl4Mv828w1ZKpAshw/uFWVDUcIB9KKFeFKoxS3cHY07FFgtTRZ1g==",
      "dev": true
      "integrity": "sha512-HNB/9GHqu7Fo8AQiugyJbv6ZxYz58wef0esl4Mv828w1ZKpAshw/uFWVDUcIB9KKFeFKoxS3cHY07FFgtTRZ1g=="
    },
    "node_modules/@types/d3-transition": {
      "version": "3.0.3",
      "resolved": "https://registry.npmjs.org/@types/d3-transition/-/d3-transition-3.0.3.tgz",
      "integrity": "sha512-/S90Od8Id1wgQNvIA8iFv9jRhCiZcGhPd2qX0bKF/PS+y0W5CrXKgIiELd2CvG1mlQrWK/qlYh3VxicqG1ZvgA==",
      "dev": true,
      "dependencies": {
        "@types/d3-selection": "*"
      }
@@ -1222,7 +1290,6 @@
      "version": "3.0.3",
      "resolved": "https://registry.npmjs.org/@types/d3-zoom/-/d3-zoom-3.0.3.tgz",
      "integrity": "sha512-OWk1yYIIWcZ07+igN6BeoG6rqhnJ/pYe+R1qWFM2DtW49zsoSjgb9G5xB0ZXA8hh2jAzey1XuRmMSoXdKw8MDA==",
      "dev": true,
      "dependencies": {
        "@types/d3-interpolate": "*",
        "@types/d3-selection": "*"
@@ -1236,6 +1303,15 @@
        "@types/ms": "*"
      }
    },
    "node_modules/@types/dompurify": {
      "version": "3.0.5",
      "resolved": "https://registry.npmjs.org/@types/dompurify/-/dompurify-3.0.5.tgz",
      "integrity": "sha512-1Wg0g3BtQF7sSb27fJQAKck1HECM6zV1EB66j8JH9i3LCjYabJa0FSdiSgsD5K/RbrsR0SiraKacLB+T8ZVYAg==",
      "license": "MIT",
      "dependencies": {
        "@types/trusted-types": "*"
      }
    },
    "node_modules/@types/earcut": {
      "version": "2.1.4",
      "resolved": "https://registry.npmjs.org/@types/earcut/-/earcut-2.1.4.tgz",
@@ -1258,8 +1334,7 @@
    "node_modules/@types/geojson": {
      "version": "7946.0.10",
      "resolved": "https://registry.npmjs.org/@types/geojson/-/geojson-7946.0.10.tgz",
      "integrity": "sha512-Nmh0K3iWQJzniTuPRcJn5hxXkfB1T1pgB89SBig5PlJQU5yocazeu4jATJlaA0GYFKWMqDdvYemoSnF2pXgLVA==",
      "dev": true
      "integrity": "sha512-Nmh0K3iWQJzniTuPRcJn5hxXkfB1T1pgB89SBig5PlJQU5yocazeu4jATJlaA0GYFKWMqDdvYemoSnF2pXgLVA=="
    },
    "node_modules/@types/hast": {
      "version": "3.0.4",
@@ -1330,6 +1405,12 @@
        "source-map": "^0.6.0"
      }
    },
    "node_modules/@types/trusted-types": {
      "version": "2.0.7",
      "resolved": "https://registry.npmjs.org/@types/trusted-types/-/trusted-types-2.0.7.tgz",
      "integrity": "sha512-ScaPdn1dQczgbl0QFTeTOmVHFULt394XJgOQNoyVhZ6r2vLnMLJfBPd53SB52T/3G36VI1/g2MZaX0cwDuXsfw==",
      "license": "MIT"
    },
    "node_modules/@types/unist": {
      "version": "2.0.6",
      "resolved": "https://registry.npmjs.org/@types/unist/-/unist-2.0.6.tgz",
@@ -1379,6 +1460,18 @@
        "node": ">=10.0.0"
      }
    },
    "node_modules/acorn": {
      "version": "8.14.0",
      "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.14.0.tgz",
      "integrity": "sha512-cl669nCJTZBsL97OF4kUQm5g5hC2uihk0NxY3WENAC0TYdILVkAyHymAntgxGkl7K+t0cXIrH5siy5S4XkFycA==",
      "license": "MIT",
      "bin": {
        "acorn": "bin/acorn"
      },
      "engines": {
        "node": ">=0.4.0"
      }
    },
    "node_modules/agent-base": {
      "version": "7.1.0",
      "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-7.1.0.tgz",
@@ -1600,6 +1693,32 @@
        "url": "https://github.com/sponsors/wooorm"
      }
    },
    "node_modules/chevrotain": {
      "version": "11.0.3",
      "resolved": "https://registry.npmjs.org/chevrotain/-/chevrotain-11.0.3.tgz",
      "integrity": "sha512-ci2iJH6LeIkvP9eJW6gpueU8cnZhv85ELY8w8WiFtNjMHA5ad6pQLaJo9mEly/9qUyCpvqX8/POVUTf18/HFdw==",
      "license": "Apache-2.0",
      "dependencies": {
        "@chevrotain/cst-dts-gen": "11.0.3",
        "@chevrotain/gast": "11.0.3",
        "@chevrotain/regexp-to-ast": "11.0.3",
        "@chevrotain/types": "11.0.3",
        "@chevrotain/utils": "11.0.3",
        "lodash-es": "4.17.21"
      }
    },
    "node_modules/chevrotain-allstar": {
      "version": "0.3.1",
      "resolved": "https://registry.npmjs.org/chevrotain-allstar/-/chevrotain-allstar-0.3.1.tgz",
      "integrity": "sha512-b7g+y9A0v4mxCW1qUhf3BSVPg+/NvGErk/dOkrDaHA0nQIQGAtrOjlX//9OQtRlSCy+x9rfB5N8yC71lH1nvMw==",
      "license": "MIT",
      "dependencies": {
        "lodash-es": "^4.17.21"
      },
      "peerDependencies": {
        "chevrotain": "^11.0.0"
      }
    },
    "node_modules/chokidar": {
      "version": "4.0.1",
      "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-4.0.1.tgz",
@@ -1748,6 +1867,12 @@
      "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz",
      "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg=="
    },
    "node_modules/confbox": {
      "version": "0.1.8",
      "resolved": "https://registry.npmjs.org/confbox/-/confbox-0.1.8.tgz",
      "integrity": "sha512-RMtmw0iFkeR4YV+fUOSucriAQNb9g8zFR52MWCtl+cCZOFRNL6zeB395vPzFhEjjn4fMxXudmELnl/KF/WrK6w==",
      "license": "MIT"
    },
    "node_modules/content-disposition": {
      "version": "0.5.2",
      "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.2.tgz",
@@ -1756,6 +1881,15 @@
        "node": ">= 0.6"
      }
    },
    "node_modules/cose-base": {
      "version": "1.0.3",
      "resolved": "https://registry.npmjs.org/cose-base/-/cose-base-1.0.3.tgz",
      "integrity": "sha512-s9whTXInMSgAp/NVXVNuVxVKzGH2qck3aQlVHxDCdAEPgtMKwc4Wq6/QKhgdEdgbLSi9rBTAcPoRa6JpiG4ksg==",
      "license": "MIT",
      "dependencies": {
        "layout-base": "^1.0.0"
      }
    },
    "node_modules/cross-fetch": {
      "version": "4.0.0",
      "resolved": "https://registry.npmjs.org/cross-fetch/-/cross-fetch-4.0.0.tgz",
@@ -1800,6 +1934,54 @@
        "node": ">=18"
      }
    },
    "node_modules/cytoscape": {
      "version": "3.30.3",
      "resolved": "https://registry.npmjs.org/cytoscape/-/cytoscape-3.30.3.tgz",
      "integrity": "sha512-HncJ9gGJbVtw7YXtIs3+6YAFSSiKsom0amWc33Z7QbylbY2JGMrA0yz4EwrdTScZxnwclXeEZHzO5pxoy0ZE4g==",
      "license": "MIT",
      "engines": {
        "node": ">=0.10"
      }
    },
    "node_modules/cytoscape-cose-bilkent": {
      "version": "4.1.0",
      "resolved": "https://registry.npmjs.org/cytoscape-cose-bilkent/-/cytoscape-cose-bilkent-4.1.0.tgz",
      "integrity": "sha512-wgQlVIUJF13Quxiv5e1gstZ08rnZj2XaLHGoFMYXz7SkNfCDOOteKBE6SYRfA9WxxI/iBc3ajfDoc6hb/MRAHQ==",
      "license": "MIT",
      "dependencies": {
        "cose-base": "^1.0.0"
      },
      "peerDependencies": {
        "cytoscape": "^3.2.0"
      }
    },
    "node_modules/cytoscape-fcose": {
      "version": "2.2.0",
      "resolved": "https://registry.npmjs.org/cytoscape-fcose/-/cytoscape-fcose-2.2.0.tgz",
      "integrity": "sha512-ki1/VuRIHFCzxWNrsshHYPs6L7TvLu3DL+TyIGEsRcvVERmxokbf5Gdk7mFxZnTdiGtnA4cfSmjZJMviqSuZrQ==",
      "license": "MIT",
      "dependencies": {
        "cose-base": "^2.2.0"
      },
      "peerDependencies": {
        "cytoscape": "^3.2.0"
      }
    },
    "node_modules/cytoscape-fcose/node_modules/cose-base": {
      "version": "2.2.0",
      "resolved": "https://registry.npmjs.org/cose-base/-/cose-base-2.2.0.tgz",
      "integrity": "sha512-AzlgcsCbUMymkADOJtQm3wO9S3ltPfYOFD5033keQn9NJzIbtnZj+UdBJe7DYml/8TdbtHJW3j58SOnKhWY/5g==",
      "license": "MIT",
      "dependencies": {
        "layout-base": "^2.0.0"
      }
    },
    "node_modules/cytoscape-fcose/node_modules/layout-base": {
      "version": "2.0.1",
      "resolved": "https://registry.npmjs.org/layout-base/-/layout-base-2.0.1.tgz",
      "integrity": "sha512-dp3s92+uNI1hWIpPGH3jK2kxE2lMjdXdr+DH8ynZHpd6PUlH6x6cbuXnoMmiNumznqaNO31xu9e79F0uuZ0JFg==",
      "license": "MIT"
    },
    "node_modules/d3": {
      "version": "7.9.0",
      "resolved": "https://registry.npmjs.org/d3/-/d3-7.9.0.tgz",
@@ -2061,6 +2243,46 @@
        "node": ">=12"
      }
    },
    "node_modules/d3-sankey": {
      "version": "0.12.3",
      "resolved": "https://registry.npmjs.org/d3-sankey/-/d3-sankey-0.12.3.tgz",
      "integrity": "sha512-nQhsBRmM19Ax5xEIPLMY9ZmJ/cDvd1BG3UVvt5h3WRxKg5zGRbvnteTyWAbzeSvlh3tW7ZEmq4VwR5mB3tutmQ==",
      "license": "BSD-3-Clause",
      "dependencies": {
        "d3-array": "1 - 2",
        "d3-shape": "^1.2.0"
      }
    },
    "node_modules/d3-sankey/node_modules/d3-array": {
      "version": "2.12.1",
      "resolved": "https://registry.npmjs.org/d3-array/-/d3-array-2.12.1.tgz",
      "integrity": "sha512-B0ErZK/66mHtEsR1TkPEEkwdy+WDesimkM5gpZr5Dsg54BiTA5RXtYW5qTLIAcekaS9xfZrzBLF/OAkB3Qn1YQ==",
      "license": "BSD-3-Clause",
      "dependencies": {
        "internmap": "^1.0.0"
      }
    },
    "node_modules/d3-sankey/node_modules/d3-path": {
      "version": "1.0.9",
      "resolved": "https://registry.npmjs.org/d3-path/-/d3-path-1.0.9.tgz",
      "integrity": "sha512-VLaYcn81dtHVTjEHd8B+pbe9yHWpXKZUC87PzoFmsFrJqgFwDe/qxfp5MlfsfM1V5E/iVt0MmEbWQ7FVIXh/bg==",
      "license": "BSD-3-Clause"
    },
    "node_modules/d3-sankey/node_modules/d3-shape": {
      "version": "1.3.7",
      "resolved": "https://registry.npmjs.org/d3-shape/-/d3-shape-1.3.7.tgz",
      "integrity": "sha512-EUkvKjqPFUAZyOlhY5gzCxCeI0Aep04LwIRpsZ/mLFelJiUfnK56jo5JMDSE7yyP2kLSb6LtF+S5chMk7uqPqw==",
      "license": "BSD-3-Clause",
      "dependencies": {
        "d3-path": "1"
      }
    },
    "node_modules/d3-sankey/node_modules/internmap": {
      "version": "1.0.1",
      "resolved": "https://registry.npmjs.org/internmap/-/internmap-1.0.1.tgz",
      "integrity": "sha512-lDB5YccMydFBtasVtxnZ3MRBHuaoE8GKsppq+EchKL2U4nK/DmEpPHNH8MZe5HkMtpSiTSOZwfN0tzYjO/lJEw==",
      "license": "ISC"
    },
    "node_modules/d3-scale": {
      "version": "4.0.2",
      "resolved": "https://registry.npmjs.org/d3-scale/-/d3-scale-4.0.2.tgz",
@@ -2170,6 +2392,16 @@
        "node": ">=12"
      }
    },
    "node_modules/dagre-d3-es": {
      "version": "7.0.11",
      "resolved": "https://registry.npmjs.org/dagre-d3-es/-/dagre-d3-es-7.0.11.tgz",
      "integrity": "sha512-tvlJLyQf834SylNKax8Wkzco/1ias1OPw8DcUMDE7oUIoSEW25riQVuiu/0OWEFqT0cxHT3Pa9/D82Jr47IONw==",
      "license": "MIT",
      "dependencies": {
        "d3": "^7.9.0",
        "lodash-es": "^4.17.21"
      }
    },
    "node_modules/data-urls": {
      "version": "5.0.0",
      "resolved": "https://registry.npmjs.org/data-urls/-/data-urls-5.0.0.tgz",
@@ -2182,12 +2414,19 @@
        "node": ">=18"
      }
    },
    "node_modules/dayjs": {
      "version": "1.11.13",
      "resolved": "https://registry.npmjs.org/dayjs/-/dayjs-1.11.13.tgz",
      "integrity": "sha512-oaMBel6gjolK862uaPQOVTA7q3TZhuSvuMQAAglQDOWYO9A91IrAOUJEyKVlqJlHE0vq5p5UXxzdPfMH/x6xNg==",
      "license": "MIT"
    },
    "node_modules/debug": {
      "version": "4.3.4",
      "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz",
      "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==",
      "version": "4.3.7",
      "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.7.tgz",
      "integrity": "sha512-Er2nc/H7RrMXZBFCEim6TCmMk02Z8vLC2Rbi1KEBggpo0fS6l0S1nnapwmIi3yW/+GOJap1Krg4w0Hg80oCqgQ==",
      "license": "MIT",
      "dependencies": {
        "ms": "2.1.2"
        "ms": "^2.1.3"
      },
      "engines": {
        "node": ">=6.0"
@@ -2262,6 +2501,12 @@
        "url": "https://github.com/sponsors/wooorm"
      }
    },
    "node_modules/dompurify": {
      "version": "3.1.6",
      "resolved": "https://registry.npmjs.org/dompurify/-/dompurify-3.1.6.tgz",
      "integrity": "sha512-cTOAhc36AalkjtBpfG6O8JimdTMWNXjiePT2xQH/ppBGi/4uIpmj8eKyIkMJErXWARyINV/sB38yf8JCLF5pbQ==",
      "license": "(MPL-2.0 OR Apache-2.0)"
    },
    "node_modules/earcut": {
      "version": "2.2.4",
      "resolved": "https://registry.npmjs.org/earcut/-/earcut-2.2.4.tgz",
@@ -2692,6 +2937,12 @@
        "js-yaml": "bin/js-yaml.js"
      }
    },
    "node_modules/hachure-fill": {
      "version": "0.5.2",
      "resolved": "https://registry.npmjs.org/hachure-fill/-/hachure-fill-0.5.2.tgz",
      "integrity": "sha512-3GKBOn+m2LX9iq+JC1064cSFprJY4jL1jCXTcpnfER5HYE2l/4EfWSGzkPa/ZDBmYI0ZOEj5VHV/eKnPGkHuOg==",
      "license": "MIT"
    },
    "node_modules/has-flag": {
      "version": "4.0.0",
      "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz",
@@ -3338,13 +3589,14 @@
      }
    },
    "node_modules/katex": {
      "version": "0.16.8",
      "resolved": "https://registry.npmjs.org/katex/-/katex-0.16.8.tgz",
      "integrity": "sha512-ftuDnJbcbOckGY11OO+zg3OofESlbR5DRl2cmN8HeWeeFIV7wTXvAOx8kEjZjobhA+9wh2fbKeO6cdcA9Mnovg==",
      "version": "0.16.11",
      "resolved": "https://registry.npmjs.org/katex/-/katex-0.16.11.tgz",
      "integrity": "sha512-RQrI8rlHY92OLf3rho/Ts8i/XvjgguEjOkO1BEXcU3N8BqPpSzBNwV/G0Ukr+P/l3ivvJUE/Fa/CwbS6HesGNQ==",
      "funding": [
        "https://opencollective.com/katex",
        "https://github.com/sponsors/katex"
      ],
      "license": "MIT",
      "dependencies": {
        "commander": "^8.3.0"
      },
@@ -3360,6 +3612,11 @@
        "node": ">= 12"
      }
    },
    "node_modules/khroma": {
      "version": "2.1.0",
      "resolved": "https://registry.npmjs.org/khroma/-/khroma-2.1.0.tgz",
      "integrity": "sha512-Ls993zuzfayK269Svk9hzpeGUKob/sIgZzyHYdjQoAdQetRKpOLj+k/QQQ/6Qi0Yz65mlROrfd+Ev+1+7dz9Kw=="
    },
    "node_modules/kind-of": {
      "version": "6.0.3",
      "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.3.tgz",
@@ -3368,6 +3625,34 @@
        "node": ">=0.10.0"
      }
    },
    "node_modules/kolorist": {
      "version": "1.8.0",
      "resolved": "https://registry.npmjs.org/kolorist/-/kolorist-1.8.0.tgz",
      "integrity": "sha512-Y+60/zizpJ3HRH8DCss+q95yr6145JXZo46OTpFvDZWLfRCE4qChOyk1b26nMaNpfHHgxagk9dXT5OP0Tfe+dQ==",
      "license": "MIT"
    },
    "node_modules/langium": {
      "version": "3.0.0",
      "resolved": "https://registry.npmjs.org/langium/-/langium-3.0.0.tgz",
      "integrity": "sha512-+Ez9EoiByeoTu/2BXmEaZ06iPNXM6thWJp02KfBO/raSMyCJ4jw7AkWWa+zBCTm0+Tw1Fj9FOxdqSskyN5nAwg==",
      "license": "MIT",
      "dependencies": {
        "chevrotain": "~11.0.3",
        "chevrotain-allstar": "~0.3.0",
        "vscode-languageserver": "~9.0.1",
        "vscode-languageserver-textdocument": "~1.0.11",
        "vscode-uri": "~3.0.8"
      },
      "engines": {
        "node": ">=16.0.0"
      }
    },
    "node_modules/layout-base": {
      "version": "1.0.2",
      "resolved": "https://registry.npmjs.org/layout-base/-/layout-base-1.0.2.tgz",
      "integrity": "sha512-8h2oVEZNktL4BH2JCOI90iD1yXwL6iNW7KcCKT2QZgQJR2vbqDsldCTPRU9NifTCqHZci57XvQQ15YTu+sTYPg==",
      "license": "MIT"
    },
    "node_modules/lightningcss": {
      "version": "1.28.1",
      "resolved": "https://registry.npmjs.org/lightningcss/-/lightningcss-1.28.1.tgz",
@@ -3585,6 +3870,28 @@
        "url": "https://opencollective.com/parcel"
      }
    },
    "node_modules/local-pkg": {
      "version": "0.5.0",
      "resolved": "https://registry.npmjs.org/local-pkg/-/local-pkg-0.5.0.tgz",
      "integrity": "sha512-ok6z3qlYyCDS4ZEU27HaU6x/xZa9Whf8jD4ptH5UZTQYZVYeb9bnZ3ojVhiJNLiXK1Hfc0GNbLXcmZ5plLDDBg==",
      "license": "MIT",
      "dependencies": {
        "mlly": "^1.4.2",
        "pkg-types": "^1.0.3"
      },
      "engines": {
        "node": ">=14"
      },
      "funding": {
        "url": "https://github.com/sponsors/antfu"
      }
    },
    "node_modules/lodash-es": {
      "version": "4.17.21",
      "resolved": "https://registry.npmjs.org/lodash-es/-/lodash-es-4.17.21.tgz",
      "integrity": "sha512-mKnC+QJ9pWVzv+C4/U3rRsHapFfHvQFoFB92e52xeyGMcX6/OlIl78je1u8vePzYZSkkogMPJ2yjxxsb89cxyw==",
      "license": "MIT"
    },
    "node_modules/longest-streak": {
      "version": "3.1.0",
      "resolved": "https://registry.npmjs.org/longest-streak/-/longest-streak-3.1.0.tgz",
@@ -3611,6 +3918,18 @@
        "url": "https://github.com/sponsors/wooorm"
      }
    },
    "node_modules/marked": {
      "version": "13.0.3",
      "resolved": "https://registry.npmjs.org/marked/-/marked-13.0.3.tgz",
      "integrity": "sha512-rqRix3/TWzE9rIoFGIn8JmsVfhiuC8VIQ8IdX5TfzmeBucdY05/0UlzKaw0eVtpcN/OdVFpBk7CjKGo9iHJ/zA==",
      "license": "MIT",
      "bin": {
        "marked": "bin/marked.js"
      },
      "engines": {
        "node": ">= 18"
      }
    },
    "node_modules/mathjax-full": {
      "version": "3.2.2",
      "resolved": "https://registry.npmjs.org/mathjax-full/-/mathjax-full-3.2.2.tgz",
@@ -4037,6 +4356,35 @@
        "node": ">= 8"
      }
    },
    "node_modules/mermaid": {
      "version": "11.4.0",
      "resolved": "https://registry.npmjs.org/mermaid/-/mermaid-11.4.0.tgz",
      "integrity": "sha512-mxCfEYvADJqOiHfGpJXLs4/fAjHz448rH0pfY5fAoxiz70rQiDSzUUy4dNET2T08i46IVpjohPd6WWbzmRHiPA==",
      "license": "MIT",
      "dependencies": {
        "@braintree/sanitize-url": "^7.0.1",
        "@iconify/utils": "^2.1.32",
        "@mermaid-js/parser": "^0.3.0",
        "@types/d3": "^7.4.3",
        "@types/dompurify": "^3.0.5",
        "cytoscape": "^3.29.2",
        "cytoscape-cose-bilkent": "^4.1.0",
        "cytoscape-fcose": "^2.2.0",
        "d3": "^7.9.0",
        "d3-sankey": "^0.12.3",
        "dagre-d3-es": "7.0.11",
        "dayjs": "^1.11.10",
        "dompurify": "^3.0.11 <3.1.7",
        "katex": "^0.16.9",
        "khroma": "^2.1.0",
        "lodash-es": "^4.17.21",
        "marked": "^13.0.2",
        "roughjs": "^4.6.6",
        "stylis": "^4.3.1",
        "ts-dedent": "^2.2.0",
        "uuid": "^9.0.1"
      }
    },
    "node_modules/mhchemparser": {
      "version": "4.2.1",
      "resolved": "https://registry.npmjs.org/mhchemparser/-/mhchemparser-4.2.1.tgz",
@@ -4673,15 +5021,28 @@
      "resolved": "https://registry.npmjs.org/mj-context-menu/-/mj-context-menu-0.6.1.tgz",
      "integrity": "sha512-7NO5s6n10TIV96d4g2uDpG7ZDpIhMh0QNfGdJw/W47JswFcosz457wqz/b5sAKvl12sxINGFCn80NZHKwxQEXA=="
    },
    "node_modules/mlly": {
      "version": "1.7.2",
      "resolved": "https://registry.npmjs.org/mlly/-/mlly-1.7.2.tgz",
      "integrity": "sha512-tN3dvVHYVz4DhSXinXIk7u9syPYaJvio118uomkovAtWBT+RdbP6Lfh/5Lvo519YMmwBafwlh20IPTXIStscpA==",
      "license": "MIT",
      "dependencies": {
        "acorn": "^8.12.1",
        "pathe": "^1.1.2",
        "pkg-types": "^1.2.0",
        "ufo": "^1.5.4"
      }
    },
    "node_modules/moo": {
      "version": "0.5.2",
      "resolved": "https://registry.npmjs.org/moo/-/moo-0.5.2.tgz",
      "integrity": "sha512-iSAJLHYKnX41mKcJKjqvnAN9sf0LMDTXDEvFv+ffuRR9a1MIuXLjMNL6EsnDHSkKLTWNqQQ5uo61P4EbU4NU+Q=="
    },
    "node_modules/ms": {
      "version": "2.1.2",
      "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz",
      "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w=="
      "version": "2.1.3",
      "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz",
      "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==",
      "license": "MIT"
    },
    "node_modules/nlcst-to-string": {
      "version": "4.0.0",
@@ -4749,6 +5110,12 @@
      "resolved": "https://registry.npmjs.org/package-json-from-dist/-/package-json-from-dist-1.0.0.tgz",
      "integrity": "sha512-dATvCeZN/8wQsGywez1mzHtTlP22H8OEfPrVMLNr4/eGa+ijtLn/6M5f0dY8UKNrC2O9UCU6SSoG3qRKnt7STw=="
    },
    "node_modules/package-manager-detector": {
      "version": "0.2.2",
      "resolved": "https://registry.npmjs.org/package-manager-detector/-/package-manager-detector-0.2.2.tgz",
      "integrity": "sha512-VgXbyrSNsml4eHWIvxxG/nTL4wgybMTXCV2Un/+yEc3aDKKU6nQBZjbeP3Pl3qm9Qg92X/1ng4ffvCeD/zwHgg==",
      "license": "MIT"
    },
    "node_modules/parse-entities": {
      "version": "4.0.1",
      "resolved": "https://registry.npmjs.org/parse-entities/-/parse-entities-4.0.1.tgz",
@@ -4812,6 +5179,12 @@
        "url": "https://github.com/inikulin/parse5?sponsor=1"
      }
    },
    "node_modules/path-data-parser": {
      "version": "0.1.0",
      "resolved": "https://registry.npmjs.org/path-data-parser/-/path-data-parser-0.1.0.tgz",
      "integrity": "sha512-NOnmBpt5Y2RWbuv0LMzsayp3lVylAHLPUTut412ZA3l+C4uw4ZVkQbjShYCQ8TCpUMdPapr4YjUqLYD6v68j+w==",
      "license": "MIT"
    },
    "node_modules/path-is-inside": {
      "version": "1.0.2",
      "resolved": "https://registry.npmjs.org/path-is-inside/-/path-is-inside-1.0.2.tgz",
@@ -4861,6 +5234,12 @@
        "url": "https://github.com/sponsors/sindresorhus"
      }
    },
    "node_modules/pathe": {
      "version": "1.1.2",
      "resolved": "https://registry.npmjs.org/pathe/-/pathe-1.1.2.tgz",
      "integrity": "sha512-whLdWMYL2TwI08hn8/ZqAbrVemu0LNaNNJZX73O6qaIdCTfXutsLhMkjdENX0qhsQ9uIimo4/aQOmXkoon2nDQ==",
      "license": "MIT"
    },
    "node_modules/picocolors": {
      "version": "1.0.0",
      "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.0.0.tgz",
@@ -4893,6 +5272,33 @@
        "parse-svg-path": "^0.1.2"
      }
    },
    "node_modules/pkg-types": {
      "version": "1.2.1",
      "resolved": "https://registry.npmjs.org/pkg-types/-/pkg-types-1.2.1.tgz",
      "integrity": "sha512-sQoqa8alT3nHjGuTjuKgOnvjo4cljkufdtLMnO2LBP/wRwuDlo1tkaEdMxCRhyGRPacv/ztlZgDPm2b7FAmEvw==",
      "license": "MIT",
      "dependencies": {
        "confbox": "^0.1.8",
        "mlly": "^1.7.2",
        "pathe": "^1.1.2"
      }
    },
    "node_modules/points-on-curve": {
      "version": "0.2.0",
      "resolved": "https://registry.npmjs.org/points-on-curve/-/points-on-curve-0.2.0.tgz",
      "integrity": "sha512-0mYKnYYe9ZcqMCWhUjItv/oHjvgEsfKvnUTg8sAtnHr3GVy7rGkXCb6d5cSyqrWqL4k81b9CPg3urd+T7aop3A==",
      "license": "MIT"
    },
    "node_modules/points-on-path": {
      "version": "0.2.1",
      "resolved": "https://registry.npmjs.org/points-on-path/-/points-on-path-0.2.1.tgz",
      "integrity": "sha512-25ClnWWuw7JbWZcgqY/gJ4FQWadKxGWk+3kR/7kD0tCaDtPPMj7oHu2ToLaVhfpnHrZzYby2w6tUA0eOIuUg8g==",
      "license": "MIT",
      "dependencies": {
        "path-data-parser": "0.1.0",
        "points-on-curve": "0.2.0"
      }
    },
    "node_modules/preact": {
      "version": "10.24.3",
      "resolved": "https://registry.npmjs.org/preact/-/preact-10.24.3.tgz",
@@ -5485,6 +5891,18 @@
      "resolved": "https://registry.npmjs.org/robust-predicates/-/robust-predicates-3.0.2.tgz",
      "integrity": "sha512-IXgzBWvWQwE6PrDI05OvmXUIruQTcoMDzRsOd5CDvHCVLcLHMTSYvOK5Cm46kWqlV3yAbuSpBZdJ5oP5OUoStg=="
    },
    "node_modules/roughjs": {
      "version": "4.6.6",
      "resolved": "https://registry.npmjs.org/roughjs/-/roughjs-4.6.6.tgz",
      "integrity": "sha512-ZUz/69+SYpFN/g/lUlo2FXcIjRkSu3nDarreVdGGndHEBJ6cXPdKguS8JGxwj5HA5xIbVKSmLgr5b3AWxtRfvQ==",
      "license": "MIT",
      "dependencies": {
        "hachure-fill": "^0.5.2",
        "path-data-parser": "^0.1.0",
        "points-on-curve": "^0.2.0",
        "points-on-path": "^0.2.1"
      }
    },
    "node_modules/rrweb-cssom": {
      "version": "0.6.0",
      "resolved": "https://registry.npmjs.org/rrweb-cssom/-/rrweb-cssom-0.6.0.tgz",
@@ -6208,6 +6626,12 @@
        "inline-style-parser": "0.2.2"
      }
    },
    "node_modules/stylis": {
      "version": "4.3.4",
      "resolved": "https://registry.npmjs.org/stylis/-/stylis-4.3.4.tgz",
      "integrity": "sha512-osIBl6BGUmSfDkyH2mB7EFvCJntXDrLhKjHTRj/rK6xLH0yuPrHULDRQzKokSOD4VoorhtKpfcfW1GAntu8now==",
      "license": "MIT"
    },
    "node_modules/supports-color": {
      "version": "8.1.1",
      "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz",
@@ -6251,6 +6675,12 @@
        "node": ">=14"
      }
    },
    "node_modules/tinyexec": {
      "version": "0.3.1",
      "resolved": "https://registry.npmjs.org/tinyexec/-/tinyexec-0.3.1.tgz",
      "integrity": "sha512-WiCJLEECkO18gwqIp6+hJg0//p23HXp4S+gGtAKu3mI2F2/sXC4FvHvXvB0zJVVaTPhx1/tOwdbRsa1sOBIKqQ==",
      "license": "MIT"
    },
    "node_modules/to-regex-range": {
      "version": "5.0.1",
      "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz",
@@ -6338,6 +6768,15 @@
        "url": "https://github.com/sponsors/wooorm"
      }
    },
    "node_modules/ts-dedent": {
      "version": "2.2.0",
      "resolved": "https://registry.npmjs.org/ts-dedent/-/ts-dedent-2.2.0.tgz",
      "integrity": "sha512-q5W7tVM71e2xjHZTlgfTDoPF/SmqKG5hddq9SzR49CH2hayqRKJtQ4mtRlSxKaJlR/+9rEM+mnBHf7I2/BQcpQ==",
      "license": "MIT",
      "engines": {
        "node": ">=6.10"
      }
    },
    "node_modules/tslib": {
      "version": "2.6.2",
      "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.2.tgz",
@@ -6782,6 +7221,12 @@
        "node": ">=14.17"
      }
    },
    "node_modules/ufo": {
      "version": "1.5.4",
      "resolved": "https://registry.npmjs.org/ufo/-/ufo-1.5.4.tgz",
      "integrity": "sha512-UsUk3byDzKd04EyoZ7U4DOlxQaD14JUKQl6/P7wiX4FNvUfm3XL246n9W5AmqwW5RSFJ27NAuM0iLscAOYUiGQ==",
      "license": "MIT"
    },
    "node_modules/undici-types": {
      "version": "6.19.8",
      "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.19.8.tgz",
@@ -6983,6 +7428,19 @@
        "requires-port": "^1.0.0"
      }
    },
    "node_modules/uuid": {
      "version": "9.0.1",
      "resolved": "https://registry.npmjs.org/uuid/-/uuid-9.0.1.tgz",
      "integrity": "sha512-b+1eJOlsR9K8HJpow9Ok3fiWOWSIcIzXodvv0rQjVoOVNpWMpxf1wZNpt4y9h10odCNrqnYp1OBzRktckBe3sA==",
      "funding": [
        "https://github.com/sponsors/broofa",
        "https://github.com/sponsors/ctavan"
      ],
      "license": "MIT",
      "bin": {
        "uuid": "dist/bin/uuid"
      }
    },
    "node_modules/varint": {
      "version": "6.0.0",
      "resolved": "https://registry.npmjs.org/varint/-/varint-6.0.0.tgz",
@@ -7050,6 +7508,55 @@
        "url": "https://opencollective.com/unified"
      }
    },
    "node_modules/vscode-jsonrpc": {
      "version": "8.2.0",
      "resolved": "https://registry.npmjs.org/vscode-jsonrpc/-/vscode-jsonrpc-8.2.0.tgz",
      "integrity": "sha512-C+r0eKJUIfiDIfwJhria30+TYWPtuHJXHtI7J0YlOmKAo7ogxP20T0zxB7HZQIFhIyvoBPwWskjxrvAtfjyZfA==",
      "license": "MIT",
      "engines": {
        "node": ">=14.0.0"
      }
    },
    "node_modules/vscode-languageserver": {
      "version": "9.0.1",
      "resolved": "https://registry.npmjs.org/vscode-languageserver/-/vscode-languageserver-9.0.1.tgz",
      "integrity": "sha512-woByF3PDpkHFUreUa7Hos7+pUWdeWMXRd26+ZX2A8cFx6v/JPTtd4/uN0/jB6XQHYaOlHbio03NTHCqrgG5n7g==",
      "license": "MIT",
      "dependencies": {
        "vscode-languageserver-protocol": "3.17.5"
      },
      "bin": {
        "installServerIntoExtension": "bin/installServerIntoExtension"
      }
    },
    "node_modules/vscode-languageserver-protocol": {
      "version": "3.17.5",
      "resolved": "https://registry.npmjs.org/vscode-languageserver-protocol/-/vscode-languageserver-protocol-3.17.5.tgz",
      "integrity": "sha512-mb1bvRJN8SVznADSGWM9u/b07H7Ecg0I3OgXDuLdn307rl/J3A9YD6/eYOssqhecL27hK1IPZAsaqh00i/Jljg==",
      "license": "MIT",
      "dependencies": {
        "vscode-jsonrpc": "8.2.0",
        "vscode-languageserver-types": "3.17.5"
      }
    },
    "node_modules/vscode-languageserver-textdocument": {
      "version": "1.0.12",
      "resolved": "https://registry.npmjs.org/vscode-languageserver-textdocument/-/vscode-languageserver-textdocument-1.0.12.tgz",
      "integrity": "sha512-cxWNPesCnQCcMPeenjKKsOCKQZ/L6Tv19DTRIGuLWe32lyzWhihGVJ/rcckZXJxfdKCFvRLS3fpBIsV/ZGX4zA==",
      "license": "MIT"
    },
    "node_modules/vscode-languageserver-types": {
      "version": "3.17.5",
      "resolved": "https://registry.npmjs.org/vscode-languageserver-types/-/vscode-languageserver-types-3.17.5.tgz",
      "integrity": "sha512-Ld1VelNuX9pdF39h2Hgaeb5hEZM2Z3jUrrMgWQAu82jMtZp7p3vJT3BzToKtZI7NgQssZje5o0zryOrhQvzQAg==",
      "license": "MIT"
    },
    "node_modules/vscode-uri": {
      "version": "3.0.8",
      "resolved": "https://registry.npmjs.org/vscode-uri/-/vscode-uri-3.0.8.tgz",
      "integrity": "sha512-AyFQ0EVmsOZOlAnxoFOGOq1SQDWAB7C6aqMGS23svWAllfOaxbuFvcT8D1i8z3Gyn8fraVeZNNmN6e9bxxXkKw==",
      "license": "MIT"
    },
    "node_modules/w3c-xmlserializer": {
      "version": "5.0.0",
      "resolved": "https://registry.npmjs.org/w3c-xmlserializer/-/w3c-xmlserializer-5.0.0.tgz",
package.json
@@ -58,6 +58,7 @@
    "mdast-util-find-and-replace": "^3.0.1",
    "mdast-util-to-hast": "^13.2.0",
    "mdast-util-to-string": "^4.0.0",
    "mermaid": "^11.4.0",
    "micromorph": "^0.4.5",
    "pixi.js": "^8.5.2",
    "preact": "^10.24.3",
quartz/components/scripts/clipboard.inline.ts
@@ -8,7 +8,9 @@
  for (let i = 0; i < els.length; i++) {
    const codeBlock = els[i].getElementsByTagName("code")[0]
    if (codeBlock) {
      const source = codeBlock.innerText.replace(/\n\n/g, "\n")
      const source = (
        codeBlock.dataset.clipboard ? JSON.parse(codeBlock.dataset.clipboard) : codeBlock.innerText
      ).replace(/\n\n/g, "\n")
      const button = document.createElement("button")
      button.className = "clipboard-button"
      button.type = "button"
quartz/components/scripts/mermaid.inline.ts
New file
@@ -0,0 +1,242 @@
import { removeAllChildren } from "./util"
import mermaid from "mermaid"
interface Position {
  x: number
  y: number
}
class DiagramPanZoom {
  private isDragging = false
  private startPan: Position = { x: 0, y: 0 }
  private currentPan: Position = { x: 0, y: 0 }
  private scale = 1
  private readonly MIN_SCALE = 0.5
  private readonly MAX_SCALE = 3
  private readonly ZOOM_SENSITIVITY = 0.001
  constructor(
    private container: HTMLElement,
    private content: HTMLElement,
  ) {
    this.setupEventListeners()
    this.setupNavigationControls()
  }
  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))
    // Wheel zoom events
    this.container.addEventListener("wheel", this.onWheel.bind(this), { passive: false })
    // Reset on window resize
    window.addEventListener("resize", this.resetTransform.bind(this))
  }
  private setupNavigationControls() {
    const controls = document.createElement("div")
    controls.className = "mermaid-controls"
    // Zoom controls
    const zoomIn = this.createButton("+", () => this.zoom(0.1))
    const zoomOut = this.createButton("-", () => this.zoom(-0.1))
    const resetBtn = this.createButton("Reset", () => this.resetTransform())
    controls.appendChild(zoomOut)
    controls.appendChild(resetBtn)
    controls.appendChild(zoomIn)
    this.container.appendChild(controls)
  }
  private createButton(text: string, onClick: () => void): HTMLButtonElement {
    const button = document.createElement("button")
    button.textContent = text
    button.className = "mermaid-control-button"
    button.addEventListener("click", onClick)
    window.addCleanup(() => button.removeEventListener("click", onClick))
    return button
  }
  private onMouseDown(e: MouseEvent) {
    if (e.button !== 0) return // Only handle left click
    this.isDragging = true
    this.startPan = { x: e.clientX - this.currentPan.x, y: e.clientY - this.currentPan.y }
    this.container.style.cursor = "grabbing"
  }
  private onMouseMove(e: MouseEvent) {
    if (!this.isDragging) return
    e.preventDefault()
    this.currentPan = {
      x: e.clientX - this.startPan.x,
      y: e.clientY - this.startPan.y,
    }
    this.updateTransform()
  }
  private onMouseUp() {
    this.isDragging = false
    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)
    // Zoom around center
    const rect = this.content.getBoundingClientRect()
    const centerX = rect.width / 2
    const centerY = rect.height / 2
    const scaleDiff = newScale - this.scale
    this.currentPan.x -= centerX * scaleDiff
    this.currentPan.y -= centerY * scaleDiff
    this.scale = newScale
    this.updateTransform()
  }
  private updateTransform() {
    this.content.style.transform = `translate(${this.currentPan.x}px, ${this.currentPan.y}px) scale(${this.scale})`
  }
  private resetTransform() {
    this.scale = 1
    this.currentPan = { x: 0, y: 0 }
    this.updateTransform()
  }
}
const cssVars = [
  "--secondary",
  "--tertiary",
  "--gray",
  "--light",
  "--lightgray",
  "--highlight",
  "--dark",
  "--darkgray",
  "--codeFont",
] as const
document.addEventListener("nav", async () => {
  const center = document.querySelector(".center") as HTMLElement
  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>,
  )
  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 })
  for (let i = 0; i < nodes.length; i++) {
    const codeBlock = nodes[i] as HTMLElement
    const pre = codeBlock.parentElement as HTMLPreElement
    const clipboardBtn = pre.querySelector(".clipboard-button") as HTMLButtonElement
    const expandBtn = pre.querySelector(".expand-button") as HTMLButtonElement
    const clipboardStyle = window.getComputedStyle(clipboardBtn)
    const clipboardWidth =
      clipboardBtn.offsetWidth +
      parseFloat(clipboardStyle.marginLeft || "0") +
      parseFloat(clipboardStyle.marginRight || "0")
    // Set expand button position
    expandBtn.style.right = `calc(${clipboardWidth}px + 0.3rem)`
    pre.prepend(expandBtn)
    // query popup container
    const popupContainer = pre.querySelector("#mermaid-container") as HTMLElement
    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
      if (!content) return
      removeAllChildren(content)
      // Clone the mermaid content
      const mermaidContent = codeBlock.querySelector("svg")!.cloneNode(true) as SVGElement
      content.appendChild(mermaidContent)
      // Show container
      popupContainer.classList.add("active")
      container.style.cursor = "grab"
      // Initialize pan-zoom after showing the popup
      panZoom = new DiagramPanZoom(container, content)
    }
    function hideMermaid() {
      popupContainer.classList.remove("active")
      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)
    document.addEventListener("keydown", handleEscape)
    window.addCleanup(() => {
      closeBtn.removeEventListener("click", hideMermaid)
      expandBtn.removeEventListener("click", showMermaid)
      document.removeEventListener("keydown", handleEscape)
    })
  }
})
quartz/components/styles/mermaid.inline.scss
New file
@@ -0,0 +1,163 @@
.expand-button {
  position: absolute;
  display: flex;
  float: right;
  padding: 0.4rem;
  margin: 0.3rem;
  right: 0; // NOTE: right will be set in mermaid.inline.ts
  color: var(--gray);
  border-color: var(--dark);
  background-color: var(--light);
  border: 1px solid;
  border-radius: 5px;
  opacity: 0;
  transition: 0.2s;
  & > svg {
    fill: var(--light);
    filter: contrast(0.3);
  }
  &:hover {
    cursor: pointer;
    border-color: var(--secondary);
  }
  &:focus {
    outline: 0;
  }
}
pre {
  &:hover > .expand-button {
    opacity: 1;
    transition: 0.2s;
  }
}
#mermaid-container {
  position: fixed;
  contain: layout;
  z-index: 999;
  left: 0;
  top: 0;
  width: 100vw;
  height: 100vh;
  overflow: hidden;
  display: none;
  backdrop-filter: blur(4px);
  background: rgba(0, 0, 0, 0.5);
  &.active {
    display: inline-block;
  }
  & > #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);
    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;
      position: relative;
      transform-origin: 0 0;
      transition: transform 0.1s ease;
      overflow: visible;
      min-height: 200px;
      min-width: 200px;
      pre {
        margin: 0;
        border: none;
      }
      svg {
        max-width: none;
        height: auto;
      }
    }
    & > .mermaid-controls {
      position: absolute;
      bottom: 20px;
      right: 20px;
      display: flex;
      gap: 8px;
      padding: 8px;
      background: var(--light);
      border: 1px solid var(--lightgray);
      border-radius: 6px;
      box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
      z-index: 2;
      .mermaid-control-button {
        display: flex;
        align-items: center;
        justify-content: center;
        width: 32px;
        height: 32px;
        padding: 0;
        border: 1px solid var(--lightgray);
        background: var(--light);
        color: var(--dark);
        border-radius: 4px;
        cursor: pointer;
        font-size: 16px;
        font-family: var(--bodyFont);
        transition: all 0.2s ease;
        &:hover {
          background: var(--lightgray);
        }
        &:active {
          transform: translateY(1px);
        }
        // Style the reset button differently
        &:nth-child(2) {
          width: auto;
          padding: 0 12px;
          font-size: 14px;
        }
      }
    }
  }
}
quartz/plugins/transformers/ofm.ts
@@ -6,11 +6,14 @@
import { SKIP, visit } from "unist-util-visit"
import path from "path"
import { splitAnchor } from "../../util/path"
import { JSResource } from "../../util/resources"
import { JSResource, CSSResource } from "../../util/resources"
// @ts-ignore
import calloutScript from "../../components/scripts/callout.inline.ts"
// @ts-ignore
import checkboxScript from "../../components/scripts/checkbox.inline.ts"
// @ts-ignore
import mermaidExtensionScript from "../../components/scripts/mermaid.inline.ts"
import mermaidStyle from "../../components/styles/mermaid.inline.scss"
import { FilePath, pathToRoot, slugTag, slugifyFilePath } from "../../util/path"
import { toHast } from "mdast-util-to-hast"
import { toHtml } from "hast-util-to-html"
@@ -279,6 +282,7 @@
                // internal link
                const url = fp + anchor
                return {
                  type: "link",
                  url,
@@ -515,6 +519,7 @@
                node.data = {
                  hProperties: {
                    className: ["mermaid"],
                    "data-clipboard": JSON.stringify(node.value),
                  },
                }
              }
@@ -659,10 +664,138 @@
        })
      }
      if (opts.mermaid) {
        plugins.push(() => {
          return (tree: HtmlRoot, _file) => {
            visit(tree, "element", (node: Element, _idx, parent) => {
              if (
                node.tagName === "code" &&
                ((node.properties?.className ?? []) as string[])?.includes("mermaid")
              ) {
                parent!.children = [
                  {
                    type: "element",
                    tagName: "button",
                    properties: {
                      className: ["expand-button"],
                      "aria-label": "Expand mermaid diagram",
                      "aria-hidden": "true",
                      "data-view-component": true,
                    },
                    children: [
                      {
                        type: "element",
                        tagName: "svg",
                        properties: {
                          width: 16,
                          height: 16,
                          viewBox: "0 0 16 16",
                          fill: "currentColor",
                        },
                        children: [
                          {
                            type: "element",
                            tagName: "path",
                            properties: {
                              fillRule: "evenodd",
                              d: "M3.72 3.72a.75.75 0 011.06 1.06L2.56 7h10.88l-2.22-2.22a.75.75 0 011.06-1.06l3.5 3.5a.75.75 0 010 1.06l-3.5 3.5a.75.75 0 11-1.06-1.06l2.22-2.22H2.56l2.22 2.22a.75.75 0 11-1.06 1.06l-3.5-3.5a.75.75 0 010-1.06l3.5-3.5z",
                            },
                            children: [],
                          },
                        ],
                      },
                    ],
                  },
                  node,
                  {
                    type: "element",
                    tagName: "div",
                    properties: { id: "mermaid-container" },
                    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",
                            properties: { className: ["mermaid-content"] },
                            children: [],
                          },
                        ],
                      },
                    ],
                  },
                ]
              }
            })
          }
        })
      }
      return plugins
    },
    externalResources() {
      const js: JSResource[] = []
      const css: CSSResource[] = []
      if (opts.enableCheckbox) {
        js.push({
@@ -682,32 +815,18 @@
      if (opts.mermaid) {
        js.push({
          script: `
          let mermaidImport = undefined
          document.addEventListener('nav', async () => {
            if (document.querySelector("code.mermaid")) {
              mermaidImport ||= await import('https://cdnjs.cloudflare.com/ajax/libs/mermaid/10.7.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' : 'default'
              })
              await mermaid.run({
                querySelector: '.mermaid'
              })
            }
          });
          `,
          script: mermaidExtensionScript,
          loadTime: "afterDOMReady",
          moduleType: "module",
          contentType: "inline",
        })
        css.push({
          content: mermaidStyle,
          inline: true,
        })
      }
      return { js }
      return { js, css }
    },
  }
}