local and global graph
This commit is contained in:
		
							
								
								
									
										8
									
								
								index.d.ts
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										8
									
								
								index.d.ts
									
									
									
									
										vendored
									
									
								
							@@ -8,10 +8,4 @@ interface CustomEventMap {
 | 
			
		||||
  "nav": CustomEvent<{ url: string }>;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
declare global {
 | 
			
		||||
  interface Document {
 | 
			
		||||
    addEventListener<K extends keyof CustomEventMap>(type: K,
 | 
			
		||||
      listener: (this: Document, ev: CustomEventMap[K]) => void): void;
 | 
			
		||||
    dispatchEvent<K extends keyof CustomEventMap>(ev: CustomEventMap[K]): void;
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
declare const fetchData: Promise<ContentIndex>
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										690
									
								
								package-lock.json
									
									
									
										generated
									
									
									
								
							
							
						
						
									
										690
									
								
								package-lock.json
									
									
									
										generated
									
									
									
								
							@@ -14,6 +14,8 @@
 | 
			
		||||
        "@napi-rs/simple-git": "^0.1.8",
 | 
			
		||||
        "chalk": "^4.1.2",
 | 
			
		||||
        "cli-spinner": "^0.2.10",
 | 
			
		||||
        "d3": "^7.8.5",
 | 
			
		||||
        "d3-force-reuse": "^1.0.1",
 | 
			
		||||
        "esbuild-sass-plugin": "^2.9.0",
 | 
			
		||||
        "github-slugger": "^2.0.0",
 | 
			
		||||
        "globby": "^13.1.4",
 | 
			
		||||
@@ -54,6 +56,7 @@
 | 
			
		||||
      },
 | 
			
		||||
      "devDependencies": {
 | 
			
		||||
        "@types/cli-spinner": "^0.2.1",
 | 
			
		||||
        "@types/d3": "^7.4.0",
 | 
			
		||||
        "@types/hast": "^2.3.4",
 | 
			
		||||
        "@types/node": "^20.1.2",
 | 
			
		||||
        "@types/pretty-time": "^1.1.2",
 | 
			
		||||
@@ -894,6 +897,259 @@
 | 
			
		||||
        "@types/node": "*"
 | 
			
		||||
      }
 | 
			
		||||
    },
 | 
			
		||||
    "node_modules/@types/d3": {
 | 
			
		||||
      "version": "7.4.0",
 | 
			
		||||
      "resolved": "https://registry.npmjs.org/@types/d3/-/d3-7.4.0.tgz",
 | 
			
		||||
      "integrity": "sha512-jIfNVK0ZlxcuRDKtRS/SypEyOQ6UHaFQBKv032X45VvxSJ6Yi5G9behy9h6tNTHTDGh5Vq+KbmBjUWLgY4meCA==",
 | 
			
		||||
      "dev": true,
 | 
			
		||||
      "dependencies": {
 | 
			
		||||
        "@types/d3-array": "*",
 | 
			
		||||
        "@types/d3-axis": "*",
 | 
			
		||||
        "@types/d3-brush": "*",
 | 
			
		||||
        "@types/d3-chord": "*",
 | 
			
		||||
        "@types/d3-color": "*",
 | 
			
		||||
        "@types/d3-contour": "*",
 | 
			
		||||
        "@types/d3-delaunay": "*",
 | 
			
		||||
        "@types/d3-dispatch": "*",
 | 
			
		||||
        "@types/d3-drag": "*",
 | 
			
		||||
        "@types/d3-dsv": "*",
 | 
			
		||||
        "@types/d3-ease": "*",
 | 
			
		||||
        "@types/d3-fetch": "*",
 | 
			
		||||
        "@types/d3-force": "*",
 | 
			
		||||
        "@types/d3-format": "*",
 | 
			
		||||
        "@types/d3-geo": "*",
 | 
			
		||||
        "@types/d3-hierarchy": "*",
 | 
			
		||||
        "@types/d3-interpolate": "*",
 | 
			
		||||
        "@types/d3-path": "*",
 | 
			
		||||
        "@types/d3-polygon": "*",
 | 
			
		||||
        "@types/d3-quadtree": "*",
 | 
			
		||||
        "@types/d3-random": "*",
 | 
			
		||||
        "@types/d3-scale": "*",
 | 
			
		||||
        "@types/d3-scale-chromatic": "*",
 | 
			
		||||
        "@types/d3-selection": "*",
 | 
			
		||||
        "@types/d3-shape": "*",
 | 
			
		||||
        "@types/d3-time": "*",
 | 
			
		||||
        "@types/d3-time-format": "*",
 | 
			
		||||
        "@types/d3-timer": "*",
 | 
			
		||||
        "@types/d3-transition": "*",
 | 
			
		||||
        "@types/d3-zoom": "*"
 | 
			
		||||
      }
 | 
			
		||||
    },
 | 
			
		||||
    "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
 | 
			
		||||
    },
 | 
			
		||||
    "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": "*"
 | 
			
		||||
      }
 | 
			
		||||
    },
 | 
			
		||||
    "node_modules/@types/d3-brush": {
 | 
			
		||||
      "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": "*"
 | 
			
		||||
      }
 | 
			
		||||
    },
 | 
			
		||||
    "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
 | 
			
		||||
    },
 | 
			
		||||
    "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
 | 
			
		||||
    },
 | 
			
		||||
    "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": "*"
 | 
			
		||||
      }
 | 
			
		||||
    },
 | 
			
		||||
    "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
 | 
			
		||||
    },
 | 
			
		||||
    "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
 | 
			
		||||
    },
 | 
			
		||||
    "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": "*"
 | 
			
		||||
      }
 | 
			
		||||
    },
 | 
			
		||||
    "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
 | 
			
		||||
    },
 | 
			
		||||
    "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
 | 
			
		||||
    },
 | 
			
		||||
    "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": "*"
 | 
			
		||||
      }
 | 
			
		||||
    },
 | 
			
		||||
    "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
 | 
			
		||||
    },
 | 
			
		||||
    "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
 | 
			
		||||
    },
 | 
			
		||||
    "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": "*"
 | 
			
		||||
      }
 | 
			
		||||
    },
 | 
			
		||||
    "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
 | 
			
		||||
    },
 | 
			
		||||
    "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": "*"
 | 
			
		||||
      }
 | 
			
		||||
    },
 | 
			
		||||
    "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
 | 
			
		||||
    },
 | 
			
		||||
    "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
 | 
			
		||||
    },
 | 
			
		||||
    "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
 | 
			
		||||
    },
 | 
			
		||||
    "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
 | 
			
		||||
    },
 | 
			
		||||
    "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": "*"
 | 
			
		||||
      }
 | 
			
		||||
    },
 | 
			
		||||
    "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
 | 
			
		||||
    },
 | 
			
		||||
    "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
 | 
			
		||||
    },
 | 
			
		||||
    "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": "*"
 | 
			
		||||
      }
 | 
			
		||||
    },
 | 
			
		||||
    "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
 | 
			
		||||
    },
 | 
			
		||||
    "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
 | 
			
		||||
    },
 | 
			
		||||
    "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
 | 
			
		||||
    },
 | 
			
		||||
    "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": "*"
 | 
			
		||||
      }
 | 
			
		||||
    },
 | 
			
		||||
    "node_modules/@types/d3-zoom": {
 | 
			
		||||
      "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": "*"
 | 
			
		||||
      }
 | 
			
		||||
    },
 | 
			
		||||
    "node_modules/@types/debug": {
 | 
			
		||||
      "version": "4.1.8",
 | 
			
		||||
      "resolved": "https://registry.npmjs.org/@types/debug/-/debug-4.1.8.tgz",
 | 
			
		||||
@@ -902,6 +1158,12 @@
 | 
			
		||||
        "@types/ms": "*"
 | 
			
		||||
      }
 | 
			
		||||
    },
 | 
			
		||||
    "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
 | 
			
		||||
    },
 | 
			
		||||
    "node_modules/@types/hast": {
 | 
			
		||||
      "version": "2.3.4",
 | 
			
		||||
      "resolved": "https://registry.npmjs.org/@types/hast/-/hast-2.3.4.tgz",
 | 
			
		||||
@@ -1290,6 +1552,408 @@
 | 
			
		||||
        "node": ">= 8"
 | 
			
		||||
      }
 | 
			
		||||
    },
 | 
			
		||||
    "node_modules/d3": {
 | 
			
		||||
      "version": "7.8.5",
 | 
			
		||||
      "resolved": "https://registry.npmjs.org/d3/-/d3-7.8.5.tgz",
 | 
			
		||||
      "integrity": "sha512-JgoahDG51ncUfJu6wX/1vWQEqOflgXyl4MaHqlcSruTez7yhaRKR9i8VjjcQGeS2en/jnFivXuaIMnseMMt0XA==",
 | 
			
		||||
      "dependencies": {
 | 
			
		||||
        "d3-array": "3",
 | 
			
		||||
        "d3-axis": "3",
 | 
			
		||||
        "d3-brush": "3",
 | 
			
		||||
        "d3-chord": "3",
 | 
			
		||||
        "d3-color": "3",
 | 
			
		||||
        "d3-contour": "4",
 | 
			
		||||
        "d3-delaunay": "6",
 | 
			
		||||
        "d3-dispatch": "3",
 | 
			
		||||
        "d3-drag": "3",
 | 
			
		||||
        "d3-dsv": "3",
 | 
			
		||||
        "d3-ease": "3",
 | 
			
		||||
        "d3-fetch": "3",
 | 
			
		||||
        "d3-force": "3",
 | 
			
		||||
        "d3-format": "3",
 | 
			
		||||
        "d3-geo": "3",
 | 
			
		||||
        "d3-hierarchy": "3",
 | 
			
		||||
        "d3-interpolate": "3",
 | 
			
		||||
        "d3-path": "3",
 | 
			
		||||
        "d3-polygon": "3",
 | 
			
		||||
        "d3-quadtree": "3",
 | 
			
		||||
        "d3-random": "3",
 | 
			
		||||
        "d3-scale": "4",
 | 
			
		||||
        "d3-scale-chromatic": "3",
 | 
			
		||||
        "d3-selection": "3",
 | 
			
		||||
        "d3-shape": "3",
 | 
			
		||||
        "d3-time": "3",
 | 
			
		||||
        "d3-time-format": "4",
 | 
			
		||||
        "d3-timer": "3",
 | 
			
		||||
        "d3-transition": "3",
 | 
			
		||||
        "d3-zoom": "3"
 | 
			
		||||
      },
 | 
			
		||||
      "engines": {
 | 
			
		||||
        "node": ">=12"
 | 
			
		||||
      }
 | 
			
		||||
    },
 | 
			
		||||
    "node_modules/d3-array": {
 | 
			
		||||
      "version": "3.2.4",
 | 
			
		||||
      "resolved": "https://registry.npmjs.org/d3-array/-/d3-array-3.2.4.tgz",
 | 
			
		||||
      "integrity": "sha512-tdQAmyA18i4J7wprpYq8ClcxZy3SC31QMeByyCFyRt7BVHdREQZ5lpzoe5mFEYZUWe+oq8HBvk9JjpibyEV4Jg==",
 | 
			
		||||
      "dependencies": {
 | 
			
		||||
        "internmap": "1 - 2"
 | 
			
		||||
      },
 | 
			
		||||
      "engines": {
 | 
			
		||||
        "node": ">=12"
 | 
			
		||||
      }
 | 
			
		||||
    },
 | 
			
		||||
    "node_modules/d3-axis": {
 | 
			
		||||
      "version": "3.0.0",
 | 
			
		||||
      "resolved": "https://registry.npmjs.org/d3-axis/-/d3-axis-3.0.0.tgz",
 | 
			
		||||
      "integrity": "sha512-IH5tgjV4jE/GhHkRV0HiVYPDtvfjHQlQfJHs0usq7M30XcSBvOotpmH1IgkcXsO/5gEQZD43B//fc7SRT5S+xw==",
 | 
			
		||||
      "engines": {
 | 
			
		||||
        "node": ">=12"
 | 
			
		||||
      }
 | 
			
		||||
    },
 | 
			
		||||
    "node_modules/d3-brush": {
 | 
			
		||||
      "version": "3.0.0",
 | 
			
		||||
      "resolved": "https://registry.npmjs.org/d3-brush/-/d3-brush-3.0.0.tgz",
 | 
			
		||||
      "integrity": "sha512-ALnjWlVYkXsVIGlOsuWH1+3udkYFI48Ljihfnh8FZPF2QS9o+PzGLBslO0PjzVoHLZ2KCVgAM8NVkXPJB2aNnQ==",
 | 
			
		||||
      "dependencies": {
 | 
			
		||||
        "d3-dispatch": "1 - 3",
 | 
			
		||||
        "d3-drag": "2 - 3",
 | 
			
		||||
        "d3-interpolate": "1 - 3",
 | 
			
		||||
        "d3-selection": "3",
 | 
			
		||||
        "d3-transition": "3"
 | 
			
		||||
      },
 | 
			
		||||
      "engines": {
 | 
			
		||||
        "node": ">=12"
 | 
			
		||||
      }
 | 
			
		||||
    },
 | 
			
		||||
    "node_modules/d3-chord": {
 | 
			
		||||
      "version": "3.0.1",
 | 
			
		||||
      "resolved": "https://registry.npmjs.org/d3-chord/-/d3-chord-3.0.1.tgz",
 | 
			
		||||
      "integrity": "sha512-VE5S6TNa+j8msksl7HwjxMHDM2yNK3XCkusIlpX5kwauBfXuyLAtNg9jCp/iHH61tgI4sb6R/EIMWCqEIdjT/g==",
 | 
			
		||||
      "dependencies": {
 | 
			
		||||
        "d3-path": "1 - 3"
 | 
			
		||||
      },
 | 
			
		||||
      "engines": {
 | 
			
		||||
        "node": ">=12"
 | 
			
		||||
      }
 | 
			
		||||
    },
 | 
			
		||||
    "node_modules/d3-color": {
 | 
			
		||||
      "version": "3.1.0",
 | 
			
		||||
      "resolved": "https://registry.npmjs.org/d3-color/-/d3-color-3.1.0.tgz",
 | 
			
		||||
      "integrity": "sha512-zg/chbXyeBtMQ1LbD/WSoW2DpC3I0mpmPdW+ynRTj/x2DAWYrIY7qeZIHidozwV24m4iavr15lNwIwLxRmOxhA==",
 | 
			
		||||
      "engines": {
 | 
			
		||||
        "node": ">=12"
 | 
			
		||||
      }
 | 
			
		||||
    },
 | 
			
		||||
    "node_modules/d3-contour": {
 | 
			
		||||
      "version": "4.0.2",
 | 
			
		||||
      "resolved": "https://registry.npmjs.org/d3-contour/-/d3-contour-4.0.2.tgz",
 | 
			
		||||
      "integrity": "sha512-4EzFTRIikzs47RGmdxbeUvLWtGedDUNkTcmzoeyg4sP/dvCexO47AaQL7VKy/gul85TOxw+IBgA8US2xwbToNA==",
 | 
			
		||||
      "dependencies": {
 | 
			
		||||
        "d3-array": "^3.2.0"
 | 
			
		||||
      },
 | 
			
		||||
      "engines": {
 | 
			
		||||
        "node": ">=12"
 | 
			
		||||
      }
 | 
			
		||||
    },
 | 
			
		||||
    "node_modules/d3-delaunay": {
 | 
			
		||||
      "version": "6.0.4",
 | 
			
		||||
      "resolved": "https://registry.npmjs.org/d3-delaunay/-/d3-delaunay-6.0.4.tgz",
 | 
			
		||||
      "integrity": "sha512-mdjtIZ1XLAM8bm/hx3WwjfHt6Sggek7qH043O8KEjDXN40xi3vx/6pYSVTwLjEgiXQTbvaouWKynLBiUZ6SK6A==",
 | 
			
		||||
      "dependencies": {
 | 
			
		||||
        "delaunator": "5"
 | 
			
		||||
      },
 | 
			
		||||
      "engines": {
 | 
			
		||||
        "node": ">=12"
 | 
			
		||||
      }
 | 
			
		||||
    },
 | 
			
		||||
    "node_modules/d3-dispatch": {
 | 
			
		||||
      "version": "3.0.1",
 | 
			
		||||
      "resolved": "https://registry.npmjs.org/d3-dispatch/-/d3-dispatch-3.0.1.tgz",
 | 
			
		||||
      "integrity": "sha512-rzUyPU/S7rwUflMyLc1ETDeBj0NRuHKKAcvukozwhshr6g6c5d8zh4c2gQjY2bZ0dXeGLWc1PF174P2tVvKhfg==",
 | 
			
		||||
      "engines": {
 | 
			
		||||
        "node": ">=12"
 | 
			
		||||
      }
 | 
			
		||||
    },
 | 
			
		||||
    "node_modules/d3-drag": {
 | 
			
		||||
      "version": "3.0.0",
 | 
			
		||||
      "resolved": "https://registry.npmjs.org/d3-drag/-/d3-drag-3.0.0.tgz",
 | 
			
		||||
      "integrity": "sha512-pWbUJLdETVA8lQNJecMxoXfH6x+mO2UQo8rSmZ+QqxcbyA3hfeprFgIT//HW2nlHChWeIIMwS2Fq+gEARkhTkg==",
 | 
			
		||||
      "dependencies": {
 | 
			
		||||
        "d3-dispatch": "1 - 3",
 | 
			
		||||
        "d3-selection": "3"
 | 
			
		||||
      },
 | 
			
		||||
      "engines": {
 | 
			
		||||
        "node": ">=12"
 | 
			
		||||
      }
 | 
			
		||||
    },
 | 
			
		||||
    "node_modules/d3-dsv": {
 | 
			
		||||
      "version": "3.0.1",
 | 
			
		||||
      "resolved": "https://registry.npmjs.org/d3-dsv/-/d3-dsv-3.0.1.tgz",
 | 
			
		||||
      "integrity": "sha512-UG6OvdI5afDIFP9w4G0mNq50dSOsXHJaRE8arAS5o9ApWnIElp8GZw1Dun8vP8OyHOZ/QJUKUJwxiiCCnUwm+Q==",
 | 
			
		||||
      "dependencies": {
 | 
			
		||||
        "commander": "7",
 | 
			
		||||
        "iconv-lite": "0.6",
 | 
			
		||||
        "rw": "1"
 | 
			
		||||
      },
 | 
			
		||||
      "bin": {
 | 
			
		||||
        "csv2json": "bin/dsv2json.js",
 | 
			
		||||
        "csv2tsv": "bin/dsv2dsv.js",
 | 
			
		||||
        "dsv2dsv": "bin/dsv2dsv.js",
 | 
			
		||||
        "dsv2json": "bin/dsv2json.js",
 | 
			
		||||
        "json2csv": "bin/json2dsv.js",
 | 
			
		||||
        "json2dsv": "bin/json2dsv.js",
 | 
			
		||||
        "json2tsv": "bin/json2dsv.js",
 | 
			
		||||
        "tsv2csv": "bin/dsv2dsv.js",
 | 
			
		||||
        "tsv2json": "bin/dsv2json.js"
 | 
			
		||||
      },
 | 
			
		||||
      "engines": {
 | 
			
		||||
        "node": ">=12"
 | 
			
		||||
      }
 | 
			
		||||
    },
 | 
			
		||||
    "node_modules/d3-dsv/node_modules/commander": {
 | 
			
		||||
      "version": "7.2.0",
 | 
			
		||||
      "resolved": "https://registry.npmjs.org/commander/-/commander-7.2.0.tgz",
 | 
			
		||||
      "integrity": "sha512-QrWXB+ZQSVPmIWIhtEO9H+gwHaMGYiF5ChvoJ+K9ZGHG/sVsa6yiesAD1GC/x46sET00Xlwo1u49RVVVzvcSkw==",
 | 
			
		||||
      "engines": {
 | 
			
		||||
        "node": ">= 10"
 | 
			
		||||
      }
 | 
			
		||||
    },
 | 
			
		||||
    "node_modules/d3-dsv/node_modules/iconv-lite": {
 | 
			
		||||
      "version": "0.6.3",
 | 
			
		||||
      "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz",
 | 
			
		||||
      "integrity": "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==",
 | 
			
		||||
      "dependencies": {
 | 
			
		||||
        "safer-buffer": ">= 2.1.2 < 3.0.0"
 | 
			
		||||
      },
 | 
			
		||||
      "engines": {
 | 
			
		||||
        "node": ">=0.10.0"
 | 
			
		||||
      }
 | 
			
		||||
    },
 | 
			
		||||
    "node_modules/d3-ease": {
 | 
			
		||||
      "version": "3.0.1",
 | 
			
		||||
      "resolved": "https://registry.npmjs.org/d3-ease/-/d3-ease-3.0.1.tgz",
 | 
			
		||||
      "integrity": "sha512-wR/XK3D3XcLIZwpbvQwQ5fK+8Ykds1ip7A2Txe0yxncXSdq1L9skcG7blcedkOX+ZcgxGAmLX1FrRGbADwzi0w==",
 | 
			
		||||
      "engines": {
 | 
			
		||||
        "node": ">=12"
 | 
			
		||||
      }
 | 
			
		||||
    },
 | 
			
		||||
    "node_modules/d3-fetch": {
 | 
			
		||||
      "version": "3.0.1",
 | 
			
		||||
      "resolved": "https://registry.npmjs.org/d3-fetch/-/d3-fetch-3.0.1.tgz",
 | 
			
		||||
      "integrity": "sha512-kpkQIM20n3oLVBKGg6oHrUchHM3xODkTzjMoj7aWQFq5QEM+R6E4WkzT5+tojDY7yjez8KgCBRoj4aEr99Fdqw==",
 | 
			
		||||
      "dependencies": {
 | 
			
		||||
        "d3-dsv": "1 - 3"
 | 
			
		||||
      },
 | 
			
		||||
      "engines": {
 | 
			
		||||
        "node": ">=12"
 | 
			
		||||
      }
 | 
			
		||||
    },
 | 
			
		||||
    "node_modules/d3-force": {
 | 
			
		||||
      "version": "3.0.0",
 | 
			
		||||
      "resolved": "https://registry.npmjs.org/d3-force/-/d3-force-3.0.0.tgz",
 | 
			
		||||
      "integrity": "sha512-zxV/SsA+U4yte8051P4ECydjD/S+qeYtnaIyAs9tgHCqfguma/aAQDjo85A9Z6EKhBirHRJHXIgJUlffT4wdLg==",
 | 
			
		||||
      "dependencies": {
 | 
			
		||||
        "d3-dispatch": "1 - 3",
 | 
			
		||||
        "d3-quadtree": "1 - 3",
 | 
			
		||||
        "d3-timer": "1 - 3"
 | 
			
		||||
      },
 | 
			
		||||
      "engines": {
 | 
			
		||||
        "node": ">=12"
 | 
			
		||||
      }
 | 
			
		||||
    },
 | 
			
		||||
    "node_modules/d3-force-reuse": {
 | 
			
		||||
      "version": "1.0.1",
 | 
			
		||||
      "resolved": "https://registry.npmjs.org/d3-force-reuse/-/d3-force-reuse-1.0.1.tgz",
 | 
			
		||||
      "integrity": "sha512-TyJfszB6JZmzOYr3oDayjm0LE1Fz0wsn9DkDcYopDOXY/M07rTTDGQ5wYQMZjmcobND3+Og53CATORFbFuQUqw==",
 | 
			
		||||
      "dependencies": {
 | 
			
		||||
        "d3-quadtree": "^1.0.3"
 | 
			
		||||
      }
 | 
			
		||||
    },
 | 
			
		||||
    "node_modules/d3-force-reuse/node_modules/d3-quadtree": {
 | 
			
		||||
      "version": "1.0.7",
 | 
			
		||||
      "resolved": "https://registry.npmjs.org/d3-quadtree/-/d3-quadtree-1.0.7.tgz",
 | 
			
		||||
      "integrity": "sha512-RKPAeXnkC59IDGD0Wu5mANy0Q2V28L+fNe65pOCXVdVuTJS3WPKaJlFHer32Rbh9gIo9qMuJXio8ra4+YmIymA=="
 | 
			
		||||
    },
 | 
			
		||||
    "node_modules/d3-format": {
 | 
			
		||||
      "version": "3.1.0",
 | 
			
		||||
      "resolved": "https://registry.npmjs.org/d3-format/-/d3-format-3.1.0.tgz",
 | 
			
		||||
      "integrity": "sha512-YyUI6AEuY/Wpt8KWLgZHsIU86atmikuoOmCfommt0LYHiQSPjvX2AcFc38PX0CBpr2RCyZhjex+NS/LPOv6YqA==",
 | 
			
		||||
      "engines": {
 | 
			
		||||
        "node": ">=12"
 | 
			
		||||
      }
 | 
			
		||||
    },
 | 
			
		||||
    "node_modules/d3-geo": {
 | 
			
		||||
      "version": "3.1.0",
 | 
			
		||||
      "resolved": "https://registry.npmjs.org/d3-geo/-/d3-geo-3.1.0.tgz",
 | 
			
		||||
      "integrity": "sha512-JEo5HxXDdDYXCaWdwLRt79y7giK8SbhZJbFWXqbRTolCHFI5jRqteLzCsq51NKbUoX0PjBVSohxrx+NoOUujYA==",
 | 
			
		||||
      "dependencies": {
 | 
			
		||||
        "d3-array": "2.5.0 - 3"
 | 
			
		||||
      },
 | 
			
		||||
      "engines": {
 | 
			
		||||
        "node": ">=12"
 | 
			
		||||
      }
 | 
			
		||||
    },
 | 
			
		||||
    "node_modules/d3-hierarchy": {
 | 
			
		||||
      "version": "3.1.2",
 | 
			
		||||
      "resolved": "https://registry.npmjs.org/d3-hierarchy/-/d3-hierarchy-3.1.2.tgz",
 | 
			
		||||
      "integrity": "sha512-FX/9frcub54beBdugHjDCdikxThEqjnR93Qt7PvQTOHxyiNCAlvMrHhclk3cD5VeAaq9fxmfRp+CnWw9rEMBuA==",
 | 
			
		||||
      "engines": {
 | 
			
		||||
        "node": ">=12"
 | 
			
		||||
      }
 | 
			
		||||
    },
 | 
			
		||||
    "node_modules/d3-interpolate": {
 | 
			
		||||
      "version": "3.0.1",
 | 
			
		||||
      "resolved": "https://registry.npmjs.org/d3-interpolate/-/d3-interpolate-3.0.1.tgz",
 | 
			
		||||
      "integrity": "sha512-3bYs1rOD33uo8aqJfKP3JWPAibgw8Zm2+L9vBKEHJ2Rg+viTR7o5Mmv5mZcieN+FRYaAOWX5SJATX6k1PWz72g==",
 | 
			
		||||
      "dependencies": {
 | 
			
		||||
        "d3-color": "1 - 3"
 | 
			
		||||
      },
 | 
			
		||||
      "engines": {
 | 
			
		||||
        "node": ">=12"
 | 
			
		||||
      }
 | 
			
		||||
    },
 | 
			
		||||
    "node_modules/d3-path": {
 | 
			
		||||
      "version": "3.1.0",
 | 
			
		||||
      "resolved": "https://registry.npmjs.org/d3-path/-/d3-path-3.1.0.tgz",
 | 
			
		||||
      "integrity": "sha512-p3KP5HCf/bvjBSSKuXid6Zqijx7wIfNW+J/maPs+iwR35at5JCbLUT0LzF1cnjbCHWhqzQTIN2Jpe8pRebIEFQ==",
 | 
			
		||||
      "engines": {
 | 
			
		||||
        "node": ">=12"
 | 
			
		||||
      }
 | 
			
		||||
    },
 | 
			
		||||
    "node_modules/d3-polygon": {
 | 
			
		||||
      "version": "3.0.1",
 | 
			
		||||
      "resolved": "https://registry.npmjs.org/d3-polygon/-/d3-polygon-3.0.1.tgz",
 | 
			
		||||
      "integrity": "sha512-3vbA7vXYwfe1SYhED++fPUQlWSYTTGmFmQiany/gdbiWgU/iEyQzyymwL9SkJjFFuCS4902BSzewVGsHHmHtXg==",
 | 
			
		||||
      "engines": {
 | 
			
		||||
        "node": ">=12"
 | 
			
		||||
      }
 | 
			
		||||
    },
 | 
			
		||||
    "node_modules/d3-quadtree": {
 | 
			
		||||
      "version": "3.0.1",
 | 
			
		||||
      "resolved": "https://registry.npmjs.org/d3-quadtree/-/d3-quadtree-3.0.1.tgz",
 | 
			
		||||
      "integrity": "sha512-04xDrxQTDTCFwP5H6hRhsRcb9xxv2RzkcsygFzmkSIOJy3PeRJP7sNk3VRIbKXcog561P9oU0/rVH6vDROAgUw==",
 | 
			
		||||
      "engines": {
 | 
			
		||||
        "node": ">=12"
 | 
			
		||||
      }
 | 
			
		||||
    },
 | 
			
		||||
    "node_modules/d3-random": {
 | 
			
		||||
      "version": "3.0.1",
 | 
			
		||||
      "resolved": "https://registry.npmjs.org/d3-random/-/d3-random-3.0.1.tgz",
 | 
			
		||||
      "integrity": "sha512-FXMe9GfxTxqd5D6jFsQ+DJ8BJS4E/fT5mqqdjovykEB2oFbTMDVdg1MGFxfQW+FBOGoB++k8swBrgwSHT1cUXQ==",
 | 
			
		||||
      "engines": {
 | 
			
		||||
        "node": ">=12"
 | 
			
		||||
      }
 | 
			
		||||
    },
 | 
			
		||||
    "node_modules/d3-scale": {
 | 
			
		||||
      "version": "4.0.2",
 | 
			
		||||
      "resolved": "https://registry.npmjs.org/d3-scale/-/d3-scale-4.0.2.tgz",
 | 
			
		||||
      "integrity": "sha512-GZW464g1SH7ag3Y7hXjf8RoUuAFIqklOAq3MRl4OaWabTFJY9PN/E1YklhXLh+OQ3fM9yS2nOkCoS+WLZ6kvxQ==",
 | 
			
		||||
      "dependencies": {
 | 
			
		||||
        "d3-array": "2.10.0 - 3",
 | 
			
		||||
        "d3-format": "1 - 3",
 | 
			
		||||
        "d3-interpolate": "1.2.0 - 3",
 | 
			
		||||
        "d3-time": "2.1.1 - 3",
 | 
			
		||||
        "d3-time-format": "2 - 4"
 | 
			
		||||
      },
 | 
			
		||||
      "engines": {
 | 
			
		||||
        "node": ">=12"
 | 
			
		||||
      }
 | 
			
		||||
    },
 | 
			
		||||
    "node_modules/d3-scale-chromatic": {
 | 
			
		||||
      "version": "3.0.0",
 | 
			
		||||
      "resolved": "https://registry.npmjs.org/d3-scale-chromatic/-/d3-scale-chromatic-3.0.0.tgz",
 | 
			
		||||
      "integrity": "sha512-Lx9thtxAKrO2Pq6OO2Ua474opeziKr279P/TKZsMAhYyNDD3EnCffdbgeSYN5O7m2ByQsxtuP2CSDczNUIZ22g==",
 | 
			
		||||
      "dependencies": {
 | 
			
		||||
        "d3-color": "1 - 3",
 | 
			
		||||
        "d3-interpolate": "1 - 3"
 | 
			
		||||
      },
 | 
			
		||||
      "engines": {
 | 
			
		||||
        "node": ">=12"
 | 
			
		||||
      }
 | 
			
		||||
    },
 | 
			
		||||
    "node_modules/d3-selection": {
 | 
			
		||||
      "version": "3.0.0",
 | 
			
		||||
      "resolved": "https://registry.npmjs.org/d3-selection/-/d3-selection-3.0.0.tgz",
 | 
			
		||||
      "integrity": "sha512-fmTRWbNMmsmWq6xJV8D19U/gw/bwrHfNXxrIN+HfZgnzqTHp9jOmKMhsTUjXOJnZOdZY9Q28y4yebKzqDKlxlQ==",
 | 
			
		||||
      "engines": {
 | 
			
		||||
        "node": ">=12"
 | 
			
		||||
      }
 | 
			
		||||
    },
 | 
			
		||||
    "node_modules/d3-shape": {
 | 
			
		||||
      "version": "3.2.0",
 | 
			
		||||
      "resolved": "https://registry.npmjs.org/d3-shape/-/d3-shape-3.2.0.tgz",
 | 
			
		||||
      "integrity": "sha512-SaLBuwGm3MOViRq2ABk3eLoxwZELpH6zhl3FbAoJ7Vm1gofKx6El1Ib5z23NUEhF9AsGl7y+dzLe5Cw2AArGTA==",
 | 
			
		||||
      "dependencies": {
 | 
			
		||||
        "d3-path": "^3.1.0"
 | 
			
		||||
      },
 | 
			
		||||
      "engines": {
 | 
			
		||||
        "node": ">=12"
 | 
			
		||||
      }
 | 
			
		||||
    },
 | 
			
		||||
    "node_modules/d3-time": {
 | 
			
		||||
      "version": "3.1.0",
 | 
			
		||||
      "resolved": "https://registry.npmjs.org/d3-time/-/d3-time-3.1.0.tgz",
 | 
			
		||||
      "integrity": "sha512-VqKjzBLejbSMT4IgbmVgDjpkYrNWUYJnbCGo874u7MMKIWsILRX+OpX/gTk8MqjpT1A/c6HY2dCA77ZN0lkQ2Q==",
 | 
			
		||||
      "dependencies": {
 | 
			
		||||
        "d3-array": "2 - 3"
 | 
			
		||||
      },
 | 
			
		||||
      "engines": {
 | 
			
		||||
        "node": ">=12"
 | 
			
		||||
      }
 | 
			
		||||
    },
 | 
			
		||||
    "node_modules/d3-time-format": {
 | 
			
		||||
      "version": "4.1.0",
 | 
			
		||||
      "resolved": "https://registry.npmjs.org/d3-time-format/-/d3-time-format-4.1.0.tgz",
 | 
			
		||||
      "integrity": "sha512-dJxPBlzC7NugB2PDLwo9Q8JiTR3M3e4/XANkreKSUxF8vvXKqm1Yfq4Q5dl8budlunRVlUUaDUgFt7eA8D6NLg==",
 | 
			
		||||
      "dependencies": {
 | 
			
		||||
        "d3-time": "1 - 3"
 | 
			
		||||
      },
 | 
			
		||||
      "engines": {
 | 
			
		||||
        "node": ">=12"
 | 
			
		||||
      }
 | 
			
		||||
    },
 | 
			
		||||
    "node_modules/d3-timer": {
 | 
			
		||||
      "version": "3.0.1",
 | 
			
		||||
      "resolved": "https://registry.npmjs.org/d3-timer/-/d3-timer-3.0.1.tgz",
 | 
			
		||||
      "integrity": "sha512-ndfJ/JxxMd3nw31uyKoY2naivF+r29V+Lc0svZxe1JvvIRmi8hUsrMvdOwgS1o6uBHmiz91geQ0ylPP0aj1VUA==",
 | 
			
		||||
      "engines": {
 | 
			
		||||
        "node": ">=12"
 | 
			
		||||
      }
 | 
			
		||||
    },
 | 
			
		||||
    "node_modules/d3-transition": {
 | 
			
		||||
      "version": "3.0.1",
 | 
			
		||||
      "resolved": "https://registry.npmjs.org/d3-transition/-/d3-transition-3.0.1.tgz",
 | 
			
		||||
      "integrity": "sha512-ApKvfjsSR6tg06xrL434C0WydLr7JewBB3V+/39RMHsaXTOG0zmt/OAXeng5M5LBm0ojmxJrpomQVZ1aPvBL4w==",
 | 
			
		||||
      "dependencies": {
 | 
			
		||||
        "d3-color": "1 - 3",
 | 
			
		||||
        "d3-dispatch": "1 - 3",
 | 
			
		||||
        "d3-ease": "1 - 3",
 | 
			
		||||
        "d3-interpolate": "1 - 3",
 | 
			
		||||
        "d3-timer": "1 - 3"
 | 
			
		||||
      },
 | 
			
		||||
      "engines": {
 | 
			
		||||
        "node": ">=12"
 | 
			
		||||
      },
 | 
			
		||||
      "peerDependencies": {
 | 
			
		||||
        "d3-selection": "2 - 3"
 | 
			
		||||
      }
 | 
			
		||||
    },
 | 
			
		||||
    "node_modules/d3-zoom": {
 | 
			
		||||
      "version": "3.0.0",
 | 
			
		||||
      "resolved": "https://registry.npmjs.org/d3-zoom/-/d3-zoom-3.0.0.tgz",
 | 
			
		||||
      "integrity": "sha512-b8AmV3kfQaqWAuacbPuNbL6vahnOJflOhexLzMMNLga62+/nh0JzvJ0aO/5a5MVgUFGS7Hu1P9P03o3fJkDCyw==",
 | 
			
		||||
      "dependencies": {
 | 
			
		||||
        "d3-dispatch": "1 - 3",
 | 
			
		||||
        "d3-drag": "2 - 3",
 | 
			
		||||
        "d3-interpolate": "1 - 3",
 | 
			
		||||
        "d3-selection": "2 - 3",
 | 
			
		||||
        "d3-transition": "2 - 3"
 | 
			
		||||
      },
 | 
			
		||||
      "engines": {
 | 
			
		||||
        "node": ">=12"
 | 
			
		||||
      }
 | 
			
		||||
    },
 | 
			
		||||
    "node_modules/debug": {
 | 
			
		||||
      "version": "4.3.4",
 | 
			
		||||
      "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz",
 | 
			
		||||
@@ -1318,6 +1982,14 @@
 | 
			
		||||
        "url": "https://github.com/sponsors/wooorm"
 | 
			
		||||
      }
 | 
			
		||||
    },
 | 
			
		||||
    "node_modules/delaunator": {
 | 
			
		||||
      "version": "5.0.0",
 | 
			
		||||
      "resolved": "https://registry.npmjs.org/delaunator/-/delaunator-5.0.0.tgz",
 | 
			
		||||
      "integrity": "sha512-AyLvtyJdbv/U1GkiS6gUUzclRoAY4Gs75qkMygJJhU75LW4DNuSF2RMzpxs9jw9Oz1BobHjTdkG3zdP55VxAqw==",
 | 
			
		||||
      "dependencies": {
 | 
			
		||||
        "robust-predicates": "^3.0.0"
 | 
			
		||||
      }
 | 
			
		||||
    },
 | 
			
		||||
    "node_modules/dequal": {
 | 
			
		||||
      "version": "2.0.3",
 | 
			
		||||
      "resolved": "https://registry.npmjs.org/dequal/-/dequal-2.0.3.tgz",
 | 
			
		||||
@@ -1965,6 +2637,14 @@
 | 
			
		||||
      "resolved": "https://registry.npmjs.org/inline-style-parser/-/inline-style-parser-0.1.1.tgz",
 | 
			
		||||
      "integrity": "sha512-7NXolsK4CAS5+xvdj5OMMbI962hU/wvwoxk+LWR9Ek9bVtyuuYScDN6eS0rUm6TxApFpw7CX1o4uJzcd4AyD3Q=="
 | 
			
		||||
    },
 | 
			
		||||
    "node_modules/internmap": {
 | 
			
		||||
      "version": "2.0.3",
 | 
			
		||||
      "resolved": "https://registry.npmjs.org/internmap/-/internmap-2.0.3.tgz",
 | 
			
		||||
      "integrity": "sha512-5Hh7Y1wQbvY5ooGgPbDaL5iYLAPzMTUrjMulskHLH6wnv/A+1q5rgEaiuqEjB+oxGXIVZs1FF+R/KPN3ZSQYYg==",
 | 
			
		||||
      "engines": {
 | 
			
		||||
        "node": ">=12"
 | 
			
		||||
      }
 | 
			
		||||
    },
 | 
			
		||||
    "node_modules/is-absolute-url": {
 | 
			
		||||
      "version": "4.0.1",
 | 
			
		||||
      "resolved": "https://registry.npmjs.org/is-absolute-url/-/is-absolute-url-4.0.1.tgz",
 | 
			
		||||
@@ -3596,6 +4276,11 @@
 | 
			
		||||
        "url": "https://github.com/sponsors/isaacs"
 | 
			
		||||
      }
 | 
			
		||||
    },
 | 
			
		||||
    "node_modules/robust-predicates": {
 | 
			
		||||
      "version": "3.0.2",
 | 
			
		||||
      "resolved": "https://registry.npmjs.org/robust-predicates/-/robust-predicates-3.0.2.tgz",
 | 
			
		||||
      "integrity": "sha512-IXgzBWvWQwE6PrDI05OvmXUIruQTcoMDzRsOd5CDvHCVLcLHMTSYvOK5Cm46kWqlV3yAbuSpBZdJ5oP5OUoStg=="
 | 
			
		||||
    },
 | 
			
		||||
    "node_modules/run-async": {
 | 
			
		||||
      "version": "3.0.0",
 | 
			
		||||
      "resolved": "https://registry.npmjs.org/run-async/-/run-async-3.0.0.tgz",
 | 
			
		||||
@@ -3626,6 +4311,11 @@
 | 
			
		||||
        "queue-microtask": "^1.2.2"
 | 
			
		||||
      }
 | 
			
		||||
    },
 | 
			
		||||
    "node_modules/rw": {
 | 
			
		||||
      "version": "1.3.3",
 | 
			
		||||
      "resolved": "https://registry.npmjs.org/rw/-/rw-1.3.3.tgz",
 | 
			
		||||
      "integrity": "sha512-PdhdWy89SiZogBLaw42zdeqtRJ//zFd2PgQavcICDUgJT5oW10QCRKbJ6bg4r0/UY2M6BWd5tkxuGFRvCkgfHQ=="
 | 
			
		||||
    },
 | 
			
		||||
    "node_modules/sade": {
 | 
			
		||||
      "version": "1.8.1",
 | 
			
		||||
      "resolved": "https://registry.npmjs.org/sade/-/sade-1.8.1.tgz",
 | 
			
		||||
 
 | 
			
		||||
@@ -30,6 +30,7 @@
 | 
			
		||||
    "@napi-rs/simple-git": "^0.1.8",
 | 
			
		||||
    "chalk": "^4.1.2",
 | 
			
		||||
    "cli-spinner": "^0.2.10",
 | 
			
		||||
    "d3": "^7.8.5",
 | 
			
		||||
    "esbuild-sass-plugin": "^2.9.0",
 | 
			
		||||
    "github-slugger": "^2.0.0",
 | 
			
		||||
    "globby": "^13.1.4",
 | 
			
		||||
@@ -67,6 +68,7 @@
 | 
			
		||||
  },
 | 
			
		||||
  "devDependencies": {
 | 
			
		||||
    "@types/cli-spinner": "^0.2.1",
 | 
			
		||||
    "@types/d3": "^7.4.0",
 | 
			
		||||
    "@types/hast": "^2.3.4",
 | 
			
		||||
    "@types/node": "^20.1.2",
 | 
			
		||||
    "@types/pretty-time": "^1.1.2",
 | 
			
		||||
 
 | 
			
		||||
@@ -4,7 +4,6 @@ import * as Plugin from "./quartz/plugins"
 | 
			
		||||
 | 
			
		||||
const config: QuartzConfig = {
 | 
			
		||||
  configuration: {
 | 
			
		||||
    siteTitle: "🪴 Quartz 4.0",
 | 
			
		||||
    enableSPA: true,
 | 
			
		||||
    ignorePatterns: ["private", "templates"],
 | 
			
		||||
    theme: {
 | 
			
		||||
@@ -58,7 +57,11 @@ const config: QuartzConfig = {
 | 
			
		||||
      Plugin.AliasRedirects(),
 | 
			
		||||
      Plugin.ContentPage({
 | 
			
		||||
        head: Component.Head(),
 | 
			
		||||
        header: [Component.PageTitle(), Component.Spacer(), Component.Darkmode()],
 | 
			
		||||
        header: [
 | 
			
		||||
          Component.PageTitle({ title: "🪴 Quartz 4.0" }),
 | 
			
		||||
          Component.Spacer(),
 | 
			
		||||
          Component.Darkmode()
 | 
			
		||||
        ],
 | 
			
		||||
        beforeBody: [
 | 
			
		||||
          Component.ArticleTitle(),
 | 
			
		||||
          Component.ReadingTime(),
 | 
			
		||||
@@ -66,9 +69,10 @@ const config: QuartzConfig = {
 | 
			
		||||
        ],
 | 
			
		||||
        content: Component.Content(),
 | 
			
		||||
        left: [
 | 
			
		||||
          Component.TableOfContents(),
 | 
			
		||||
        ],
 | 
			
		||||
        right: [
 | 
			
		||||
          Component.Graph(),
 | 
			
		||||
          Component.TableOfContents(),
 | 
			
		||||
        ],
 | 
			
		||||
        footer: []
 | 
			
		||||
      }),
 | 
			
		||||
 
 | 
			
		||||
@@ -2,7 +2,6 @@ import { PluginTypes } from "./plugins/types"
 | 
			
		||||
import { Theme } from "./theme"
 | 
			
		||||
 | 
			
		||||
export interface GlobalConfiguration {
 | 
			
		||||
  siteTitle: string,
 | 
			
		||||
  /** Whether to enable single-page-app style rendering. this prevents flashes of unstyled content and improves smoothness of Quartz */
 | 
			
		||||
  enableSPA: boolean,
 | 
			
		||||
  /** Glob patterns to not search */
 | 
			
		||||
 
 | 
			
		||||
@@ -4,10 +4,15 @@ function ArticleTitle({ fileData }: QuartzComponentProps) {
 | 
			
		||||
  const title = fileData.frontmatter?.title
 | 
			
		||||
  const displayTitle = fileData.slug === "index" ? undefined : title
 | 
			
		||||
  if (displayTitle) {
 | 
			
		||||
    return <h1>{displayTitle}</h1>
 | 
			
		||||
    return <h1 class="article-title">{displayTitle}</h1>
 | 
			
		||||
  } else {
 | 
			
		||||
    return null
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
ArticleTitle.css = `
 | 
			
		||||
.article-title {
 | 
			
		||||
  margin: 2rem 0 0 0;
 | 
			
		||||
}
 | 
			
		||||
`
 | 
			
		||||
 | 
			
		||||
export default (() => ArticleTitle) satisfies QuartzComponentConstructor
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										81
									
								
								quartz/components/Graph.tsx
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										81
									
								
								quartz/components/Graph.tsx
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,81 @@
 | 
			
		||||
import { QuartzComponentConstructor } from "./types"
 | 
			
		||||
// @ts-ignore
 | 
			
		||||
import script from "./scripts/graph.inline"
 | 
			
		||||
import style from "./styles/graph.scss"
 | 
			
		||||
 | 
			
		||||
export interface D3Config {
 | 
			
		||||
  drag: boolean,
 | 
			
		||||
  zoom: boolean,
 | 
			
		||||
  depth: number,
 | 
			
		||||
  scale: number,
 | 
			
		||||
  repelForce: number,
 | 
			
		||||
  centerForce: number,
 | 
			
		||||
  linkDistance: number,
 | 
			
		||||
  fontSize: number,
 | 
			
		||||
  opacityScale: number
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
interface GraphOptions {
 | 
			
		||||
  localGraph: Partial<D3Config>,
 | 
			
		||||
  globalGraph: Partial<D3Config> | undefined
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
const defaultOptions: GraphOptions = {
 | 
			
		||||
  localGraph: {
 | 
			
		||||
    drag: true,
 | 
			
		||||
    zoom: true,
 | 
			
		||||
    depth: 1,
 | 
			
		||||
    scale: 1.2,
 | 
			
		||||
    repelForce: 2,
 | 
			
		||||
    centerForce: 1,
 | 
			
		||||
    linkDistance: 30,
 | 
			
		||||
    fontSize: 0.6,
 | 
			
		||||
    opacityScale: 3
 | 
			
		||||
  },
 | 
			
		||||
  globalGraph: {
 | 
			
		||||
    drag: true,
 | 
			
		||||
    zoom: true,
 | 
			
		||||
    depth: -1,
 | 
			
		||||
    scale: 1.2,
 | 
			
		||||
    repelForce: 1,
 | 
			
		||||
    centerForce: 1,
 | 
			
		||||
    linkDistance: 30,
 | 
			
		||||
    fontSize: 0.5,
 | 
			
		||||
    opacityScale: 3
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export default ((opts?: GraphOptions) => {
 | 
			
		||||
  function Graph() {
 | 
			
		||||
    const localGraph = { ...opts?.localGraph, ...defaultOptions.localGraph }
 | 
			
		||||
    const globalGraph = { ...opts?.globalGraph, ...defaultOptions.globalGraph }
 | 
			
		||||
    return <div class="graph">
 | 
			
		||||
      <h3>Interactive Graph</h3>
 | 
			
		||||
      <div class="graph-outer">
 | 
			
		||||
        <div id="graph-container" data-cfg={JSON.stringify(localGraph)}></div>
 | 
			
		||||
        <svg version="1.1" id="global-graph-icon" xmlns="http://www.w3.org/2000/svg" xmlnsXlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
 | 
			
		||||
          viewBox="0 0 55 55" fill="currentColor" xmlSpace="preserve">
 | 
			
		||||
          <path d="M49,0c-3.309,0-6,2.691-6,6c0,1.035,0.263,2.009,0.726,2.86l-9.829,9.829C32.542,17.634,30.846,17,29,17
 | 
			
		||||
	s-3.542,0.634-4.898,1.688l-7.669-7.669C16.785,10.424,17,9.74,17,9c0-2.206-1.794-4-4-4S9,6.794,9,9s1.794,4,4,4
 | 
			
		||||
	c0.74,0,1.424-0.215,2.019-0.567l7.669,7.669C21.634,21.458,21,23.154,21,25s0.634,3.542,1.688,4.897L10.024,42.562
 | 
			
		||||
	C8.958,41.595,7.549,41,6,41c-3.309,0-6,2.691-6,6s2.691,6,6,6s6-2.691,6-6c0-1.035-0.263-2.009-0.726-2.86l12.829-12.829
 | 
			
		||||
	c1.106,0.86,2.44,1.436,3.898,1.619v10.16c-2.833,0.478-5,2.942-5,5.91c0,3.309,2.691,6,6,6s6-2.691,6-6c0-2.967-2.167-5.431-5-5.91
 | 
			
		||||
	v-10.16c1.458-0.183,2.792-0.759,3.898-1.619l7.669,7.669C41.215,39.576,41,40.26,41,41c0,2.206,1.794,4,4,4s4-1.794,4-4
 | 
			
		||||
	s-1.794-4-4-4c-0.74,0-1.424,0.215-2.019,0.567l-7.669-7.669C36.366,28.542,37,26.846,37,25s-0.634-3.542-1.688-4.897l9.665-9.665
 | 
			
		||||
	C46.042,11.405,47.451,12,49,12c3.309,0,6-2.691,6-6S52.309,0,49,0z M11,9c0-1.103,0.897-2,2-2s2,0.897,2,2s-0.897,2-2,2
 | 
			
		||||
	S11,10.103,11,9z M6,51c-2.206,0-4-1.794-4-4s1.794-4,4-4s4,1.794,4,4S8.206,51,6,51z M33,49c0,2.206-1.794,4-4,4s-4-1.794-4-4
 | 
			
		||||
	s1.794-4,4-4S33,46.794,33,49z M29,31c-3.309,0-6-2.691-6-6s2.691-6,6-6s6,2.691,6,6S32.309,31,29,31z M47,41c0,1.103-0.897,2-2,2
 | 
			
		||||
	s-2-0.897-2-2s0.897-2,2-2S47,39.897,47,41z M49,10c-2.206,0-4-1.794-4-4s1.794-4,4-4s4,1.794,4,4S51.206,10,49,10z"/>
 | 
			
		||||
        </svg>
 | 
			
		||||
      </div>
 | 
			
		||||
      <div id="global-graph-outer">
 | 
			
		||||
        <div id="global-graph-container" data-cfg={JSON.stringify(globalGraph)}></div>
 | 
			
		||||
      </div>
 | 
			
		||||
    </div>
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  Graph.css = style
 | 
			
		||||
  Graph.afterDOMLoaded = script
 | 
			
		||||
 | 
			
		||||
  return Graph
 | 
			
		||||
}) satisfies QuartzComponentConstructor
 | 
			
		||||
@@ -2,6 +2,15 @@ import { resolveToRoot } from "../path"
 | 
			
		||||
import { JSResourceToScriptElement } from "../resources"
 | 
			
		||||
import { QuartzComponentConstructor, QuartzComponentProps } from "./types"
 | 
			
		||||
 | 
			
		||||
interface Options {
 | 
			
		||||
  prefetchContentIndex: boolean
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
const defaultOptions: Options = {
 | 
			
		||||
  prefetchContentIndex: true
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export default ((opts?: Options) => {
 | 
			
		||||
  function Head({ fileData, externalResources }: QuartzComponentProps) {
 | 
			
		||||
    const slug = fileData.slug!
 | 
			
		||||
    const title = fileData.frontmatter?.title ?? "Untitled"
 | 
			
		||||
@@ -11,6 +20,10 @@ function Head({ fileData, externalResources }: QuartzComponentProps) {
 | 
			
		||||
    const iconPath = baseDir + "/static/icon.png"
 | 
			
		||||
    const ogImagePath = baseDir + "/static/og-image.png"
 | 
			
		||||
 | 
			
		||||
    const prefetchContentIndex = opts?.prefetchContentIndex ?? defaultOptions.prefetchContentIndex
 | 
			
		||||
    const contentIndexPath = baseDir + "/static/contentIndex.json"
 | 
			
		||||
    const contentIndexScript = `const fetchData = fetch(\`${contentIndexPath}\`).then(data => data.json())`
 | 
			
		||||
 | 
			
		||||
    return <head>
 | 
			
		||||
      <title>{title}</title>
 | 
			
		||||
      <meta charSet="utf-8" />
 | 
			
		||||
@@ -25,9 +38,11 @@ function Head({ fileData, externalResources }: QuartzComponentProps) {
 | 
			
		||||
      <meta name="generator" content="Quartz" />
 | 
			
		||||
      <link rel="preconnect" href="https://fonts.googleapis.com" />
 | 
			
		||||
      <link rel="preconnect" href="https://fonts.gstatic.com" />
 | 
			
		||||
      {prefetchContentIndex && <script spa-preserve>{contentIndexScript}</script>}
 | 
			
		||||
      {css.map(href => <link key={href} href={href} rel="stylesheet" type="text/css" spa-preserve />)}
 | 
			
		||||
      {js.filter(resource => resource.loadTime === "beforeDOMReady").map(res => JSResourceToScriptElement(res, true))}
 | 
			
		||||
    </head>
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
export default (() => Head) satisfies QuartzComponentConstructor
 | 
			
		||||
  return Head
 | 
			
		||||
}) satisfies QuartzComponentConstructor
 | 
			
		||||
 
 | 
			
		||||
@@ -1,9 +1,9 @@
 | 
			
		||||
import { QuartzComponentConstructor, QuartzComponentProps } from "./types"
 | 
			
		||||
 | 
			
		||||
function Header({ children }: QuartzComponentProps) {
 | 
			
		||||
  return <header>
 | 
			
		||||
  return (children.length > 0) ? <header>
 | 
			
		||||
    {children}
 | 
			
		||||
  </header>
 | 
			
		||||
  </header> : null
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
Header.css = `
 | 
			
		||||
@@ -11,12 +11,10 @@ header {
 | 
			
		||||
  display: flex;
 | 
			
		||||
  flex-direction: row;
 | 
			
		||||
  align-items: center;
 | 
			
		||||
  margin: 1em 0 2em 0;
 | 
			
		||||
  & > h1 {
 | 
			
		||||
  }
 | 
			
		||||
  margin: 2em 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
header > h1 {
 | 
			
		||||
header h1 {
 | 
			
		||||
  margin: 0;
 | 
			
		||||
  flex: auto;
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -1,11 +1,22 @@
 | 
			
		||||
import { resolveToRoot } from "../path"
 | 
			
		||||
import { QuartzComponentConstructor, QuartzComponentProps } from "./types"
 | 
			
		||||
 | 
			
		||||
function PageTitle({ cfg, fileData }: QuartzComponentProps) {
 | 
			
		||||
  const title = cfg.siteTitle
 | 
			
		||||
  const slug = fileData.slug!
 | 
			
		||||
  const baseDir = resolveToRoot(slug)
 | 
			
		||||
  return <h1><a href={baseDir}>{title}</a></h1>
 | 
			
		||||
interface Options {
 | 
			
		||||
  title: string
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export default (() => PageTitle) satisfies QuartzComponentConstructor
 | 
			
		||||
export default ((opts?: Options) => {
 | 
			
		||||
  const title = opts?.title ?? "Untitled Quartz"
 | 
			
		||||
  function PageTitle({ fileData }: QuartzComponentProps) {
 | 
			
		||||
    const slug = fileData.slug!
 | 
			
		||||
    const baseDir = resolveToRoot(slug)
 | 
			
		||||
    return <h1 class="page-title"><a href={baseDir}>{title}</a></h1>
 | 
			
		||||
  }
 | 
			
		||||
  PageTitle.css = `
 | 
			
		||||
  .page-title {
 | 
			
		||||
    margin: 0;
 | 
			
		||||
  }
 | 
			
		||||
  `
 | 
			
		||||
 | 
			
		||||
  return PageTitle
 | 
			
		||||
}) satisfies QuartzComponentConstructor
 | 
			
		||||
 
 | 
			
		||||
@@ -18,7 +18,7 @@ function TableOfContents({ fileData }: QuartzComponentProps) {
 | 
			
		||||
    return null
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  return <>
 | 
			
		||||
  return <div>
 | 
			
		||||
    <button type="button" id="toc">
 | 
			
		||||
      <h3>Table of Contents</h3>
 | 
			
		||||
      <svg 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" class="fold">
 | 
			
		||||
@@ -32,7 +32,7 @@ function TableOfContents({ fileData }: QuartzComponentProps) {
 | 
			
		||||
        </li>)}
 | 
			
		||||
      </ul>
 | 
			
		||||
    </div>
 | 
			
		||||
  </>
 | 
			
		||||
  </div>
 | 
			
		||||
}
 | 
			
		||||
TableOfContents.css = modernStyle
 | 
			
		||||
TableOfContents.afterDOMLoaded = script
 | 
			
		||||
 
 | 
			
		||||
@@ -7,6 +7,7 @@ import ReadingTime from "./ReadingTime"
 | 
			
		||||
import Spacer from "./Spacer"
 | 
			
		||||
import TableOfContents from "./TableOfContents"
 | 
			
		||||
import TagList from "./TagList"
 | 
			
		||||
import Graph from "./Graph" 
 | 
			
		||||
 | 
			
		||||
export {
 | 
			
		||||
  ArticleTitle,
 | 
			
		||||
@@ -17,5 +18,6 @@ export {
 | 
			
		||||
  ReadingTime,
 | 
			
		||||
  Spacer,
 | 
			
		||||
  TableOfContents,
 | 
			
		||||
  TagList
 | 
			
		||||
  TagList,
 | 
			
		||||
  Graph
 | 
			
		||||
} 
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										287
									
								
								quartz/components/scripts/graph.inline.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										287
									
								
								quartz/components/scripts/graph.inline.ts
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,287 @@
 | 
			
		||||
import { ContentDetails } from "../../plugins/emitters/contentIndex"
 | 
			
		||||
import * as d3 from 'd3'
 | 
			
		||||
 | 
			
		||||
type NodeData = {
 | 
			
		||||
  id: string,
 | 
			
		||||
  text: string,
 | 
			
		||||
  tags: string[]
 | 
			
		||||
} & d3.SimulationNodeDatum
 | 
			
		||||
 | 
			
		||||
type LinkData = {
 | 
			
		||||
  source: string,
 | 
			
		||||
  target: string
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
function relative(from: string, to: string) {
 | 
			
		||||
  const pieces = [location.protocol, '//', location.host, location.pathname]
 | 
			
		||||
  const url = pieces.join('').slice(0, -from.length) + to
 | 
			
		||||
  return url
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
function removeAllChildren(node: HTMLElement) {
 | 
			
		||||
  while (node.firstChild) {
 | 
			
		||||
    node.removeChild(node.firstChild)
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
async function renderGraph(container: string, slug: string) {
 | 
			
		||||
  const graph = document.getElementById(container)!
 | 
			
		||||
  removeAllChildren(graph)
 | 
			
		||||
 | 
			
		||||
  let {
 | 
			
		||||
    drag: enableDrag,
 | 
			
		||||
    zoom: enableZoom,
 | 
			
		||||
    depth,
 | 
			
		||||
    scale,
 | 
			
		||||
    repelForce,
 | 
			
		||||
    centerForce,
 | 
			
		||||
    linkDistance,
 | 
			
		||||
    fontSize,
 | 
			
		||||
    opacityScale
 | 
			
		||||
  } = JSON.parse(graph.dataset["cfg"]!)
 | 
			
		||||
 | 
			
		||||
  const data = await fetchData
 | 
			
		||||
 | 
			
		||||
  const links: LinkData[] = []
 | 
			
		||||
  for (const [src, details] of Object.entries<ContentDetails>(data)) {
 | 
			
		||||
    const outgoing = details.links ?? []
 | 
			
		||||
    for (const dest of outgoing) {
 | 
			
		||||
      if (src in data && dest in data) {
 | 
			
		||||
        links.push({ source: src, target: dest })
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  const neighbourhood = new Set()
 | 
			
		||||
 | 
			
		||||
  const wl = [slug, "__SENTINEL"]
 | 
			
		||||
  if (depth >= 0) {
 | 
			
		||||
    while (depth >= 0 && wl.length > 0) {
 | 
			
		||||
      // compute neighbours
 | 
			
		||||
      const cur = wl.shift()
 | 
			
		||||
      if (cur === "__SENTINEL") {
 | 
			
		||||
        depth--
 | 
			
		||||
        wl.push("__SENTINEL")
 | 
			
		||||
      } else {
 | 
			
		||||
        neighbourhood.add(cur)
 | 
			
		||||
        const outgoing = links.filter(l => l.source === cur)
 | 
			
		||||
        const incoming = links.filter(l => l.target === cur)
 | 
			
		||||
        wl.push(...outgoing.map((l) => l.target), ...incoming.map((l) => l.source))
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
  } else {
 | 
			
		||||
    links.flatMap(l => [l.source, l.target]).forEach((id) => neighbourhood.add(id))
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  const graphData: { nodes: NodeData[], links: LinkData[] } = {
 | 
			
		||||
    nodes: Object.keys(data).filter(id => neighbourhood.has(id)).map(url => ({ id: url, text: data[url]?.title ?? url, tags: data[url]?.tags ?? [] })),
 | 
			
		||||
    links: links.filter((l) => neighbourhood.has(l.source) && neighbourhood.has(l.target))
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  const simulation: d3.Simulation<NodeData, LinkData> = d3
 | 
			
		||||
    .forceSimulation(graphData.nodes)
 | 
			
		||||
    .force("charge", d3.forceManyBody().strength(-100 * repelForce))
 | 
			
		||||
    .force(
 | 
			
		||||
      "link",
 | 
			
		||||
      d3
 | 
			
		||||
        .forceLink(graphData.links)
 | 
			
		||||
        .id((d: any) => d.id)
 | 
			
		||||
        .distance(linkDistance),
 | 
			
		||||
    )
 | 
			
		||||
    .force("center", d3.forceCenter().strength(centerForce))
 | 
			
		||||
 | 
			
		||||
  const height = Math.max(graph.offsetHeight, 250)
 | 
			
		||||
  const width = graph.offsetWidth
 | 
			
		||||
 | 
			
		||||
  const svg = d3
 | 
			
		||||
    .select<HTMLElement, NodeData>('#' + container)
 | 
			
		||||
    .append("svg")
 | 
			
		||||
    .attr("width", width)
 | 
			
		||||
    .attr("height", height)
 | 
			
		||||
    .attr('viewBox', [-width / 2 / scale, -height / 2 / scale, width / scale, height / scale])
 | 
			
		||||
 | 
			
		||||
  // draw links between nodes
 | 
			
		||||
  const link = svg
 | 
			
		||||
    .append("g")
 | 
			
		||||
    .selectAll("line")
 | 
			
		||||
    .data(graphData.links)
 | 
			
		||||
    .join("line")
 | 
			
		||||
    .attr("class", "link")
 | 
			
		||||
    .attr("stroke", "var(--lightgray)")
 | 
			
		||||
    .attr("stroke-width", 2)
 | 
			
		||||
 | 
			
		||||
  // svg groups
 | 
			
		||||
  const graphNode = svg.append("g").selectAll("g").data(graphData.nodes).enter().append("g")
 | 
			
		||||
 | 
			
		||||
  // calculate radius
 | 
			
		||||
  const color = (d: NodeData) => {
 | 
			
		||||
    // TODO: does this handle the index page
 | 
			
		||||
    const isCurrent = d.id === slug
 | 
			
		||||
    return isCurrent ? "var(--secondary)" : "var(--gray)"
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  const drag = (simulation: d3.Simulation<NodeData, LinkData>) => {
 | 
			
		||||
    function dragstarted(event: any, d: NodeData) {
 | 
			
		||||
      if (!event.active) simulation.alphaTarget(1).restart()
 | 
			
		||||
      d.fx = d.x
 | 
			
		||||
      d.fy = d.y
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    function dragged(event: any, d: NodeData) {
 | 
			
		||||
      d.fx = event.x
 | 
			
		||||
      d.fy = event.y
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    function dragended(event: any, d: NodeData) {
 | 
			
		||||
      if (!event.active) simulation.alphaTarget(0)
 | 
			
		||||
      d.fx = null
 | 
			
		||||
      d.fy = null
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    const noop = () => { }
 | 
			
		||||
    return d3
 | 
			
		||||
      .drag<Element, NodeData>()
 | 
			
		||||
      .on("start", enableDrag ? dragstarted : noop)
 | 
			
		||||
      .on("drag", enableDrag ? dragged : noop)
 | 
			
		||||
      .on("end", enableDrag ? dragended : noop)
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  function nodeRadius(d: NodeData) {
 | 
			
		||||
    const numLinks = links.filter((l: any) => l.source.id === d.id || l.target.id === d.id).length
 | 
			
		||||
    return 2 + Math.sqrt(numLinks)
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  // draw individual nodes
 | 
			
		||||
  const node = graphNode
 | 
			
		||||
    .append("circle")
 | 
			
		||||
    .attr("class", "node")
 | 
			
		||||
    .attr("id", (d) => d.id)
 | 
			
		||||
    .attr("r", nodeRadius)
 | 
			
		||||
    .attr("fill", color)
 | 
			
		||||
    .style("cursor", "pointer")
 | 
			
		||||
    .on("click", (_, d) => {
 | 
			
		||||
      const targ = relative(slug, d.id)
 | 
			
		||||
      window.spaNavigate(new URL(targ))
 | 
			
		||||
    })
 | 
			
		||||
    .on("mouseover", function(_, d) {
 | 
			
		||||
      const neighbours: string[] = data[slug].links ?? []
 | 
			
		||||
      const neighbourNodes = d3.selectAll<HTMLElement, NodeData>(".node").filter((d) => neighbours.includes(d.id))
 | 
			
		||||
      const currentId = d.id
 | 
			
		||||
      const linkNodes = d3
 | 
			
		||||
        .selectAll(".link")
 | 
			
		||||
        .filter((d: any) => d.source.id === currentId || d.target.id === currentId)
 | 
			
		||||
 | 
			
		||||
      // highlight neighbour nodes
 | 
			
		||||
      neighbourNodes.transition().duration(200).attr("fill", color)
 | 
			
		||||
 | 
			
		||||
      // highlight links
 | 
			
		||||
      linkNodes.transition().duration(200).attr("stroke", "var(--gray)")
 | 
			
		||||
 | 
			
		||||
      const bigFont = fontSize * 1.5
 | 
			
		||||
 | 
			
		||||
      // show text for self
 | 
			
		||||
      const parent = this.parentNode as HTMLElement
 | 
			
		||||
      d3.select<HTMLElement, NodeData>(parent)
 | 
			
		||||
        .raise()
 | 
			
		||||
        .select("text")
 | 
			
		||||
        .transition()
 | 
			
		||||
        .duration(200)
 | 
			
		||||
        .attr('opacityOld', d3.select(parent).select('text').style("opacity"))
 | 
			
		||||
        .style('opacity', 1)
 | 
			
		||||
        .style('font-size', bigFont + 'em')
 | 
			
		||||
    })
 | 
			
		||||
    .on("mouseleave", function(_, d) {
 | 
			
		||||
      const currentId = d.id
 | 
			
		||||
      const linkNodes = d3
 | 
			
		||||
        .selectAll(".link")
 | 
			
		||||
        .filter((d: any) => d.source.id === currentId || d.target.id === currentId)
 | 
			
		||||
 | 
			
		||||
      linkNodes.transition().duration(200).attr("stroke", "var(--lightgray)")
 | 
			
		||||
 | 
			
		||||
      const parent = this.parentNode as HTMLElement
 | 
			
		||||
      d3.select<HTMLElement, NodeData>(parent)
 | 
			
		||||
        .select("text")
 | 
			
		||||
        .transition()
 | 
			
		||||
        .duration(200)
 | 
			
		||||
        .style('opacity', d3.select(parent).select('text').attr("opacityOld"))
 | 
			
		||||
        .style('font-size', fontSize + 'em')
 | 
			
		||||
    })
 | 
			
		||||
    // @ts-ignore
 | 
			
		||||
    .call(drag(simulation))
 | 
			
		||||
 | 
			
		||||
  // draw labels
 | 
			
		||||
  const labels = graphNode
 | 
			
		||||
    .append("text")
 | 
			
		||||
    .attr("dx", 0)
 | 
			
		||||
    .attr("dy", (d) => nodeRadius(d) + 8 + "px")
 | 
			
		||||
    .attr("text-anchor", "middle")
 | 
			
		||||
    .text((d) => data[d.id]?.title || (d.id.charAt(1).toUpperCase() + d.id.slice(2)).replace("-", " "))
 | 
			
		||||
    .style('opacity', (opacityScale - 1) / 3.75)
 | 
			
		||||
    .style("pointer-events", "none")
 | 
			
		||||
    .style('font-size', fontSize + 'em')
 | 
			
		||||
    .raise()
 | 
			
		||||
    // @ts-ignore
 | 
			
		||||
    .call(drag(simulation))
 | 
			
		||||
 | 
			
		||||
  // set panning
 | 
			
		||||
  if (enableZoom) {
 | 
			
		||||
    svg.call(
 | 
			
		||||
      d3
 | 
			
		||||
        .zoom<SVGSVGElement, NodeData>()
 | 
			
		||||
        .extent([
 | 
			
		||||
          [0, 0],
 | 
			
		||||
          [width, height],
 | 
			
		||||
        ])
 | 
			
		||||
        .scaleExtent([0.25, 4])
 | 
			
		||||
        .on("zoom", ({ transform }) => {
 | 
			
		||||
          link.attr("transform", transform)
 | 
			
		||||
          node.attr("transform", transform)
 | 
			
		||||
          const scale = transform.k * opacityScale;
 | 
			
		||||
          const scaledOpacity = Math.max((scale - 1) / 3.75, 0)
 | 
			
		||||
          labels.attr("transform", transform).style("opacity", scaledOpacity)
 | 
			
		||||
        }),
 | 
			
		||||
    )
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  // progress the simulation
 | 
			
		||||
  simulation.on("tick", () => {
 | 
			
		||||
    link
 | 
			
		||||
      .attr("x1", (d: any) => d.source.x)
 | 
			
		||||
      .attr("y1", (d: any) => d.source.y)
 | 
			
		||||
      .attr("x2", (d: any) => d.target.x)
 | 
			
		||||
      .attr("y2", (d: any) => d.target.y)
 | 
			
		||||
    node
 | 
			
		||||
      .attr("cx", (d: any) => d.x)
 | 
			
		||||
      .attr("cy", (d: any) => d.y)
 | 
			
		||||
    labels
 | 
			
		||||
      .attr("x", (d: any) => d.x)
 | 
			
		||||
      .attr("y", (d: any) => d.y)
 | 
			
		||||
  })
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
function renderGlobalGraph() {
 | 
			
		||||
  const slug = document.body.dataset["slug"]!
 | 
			
		||||
  renderGraph("global-graph-container", slug)
 | 
			
		||||
  const container = document.getElementById("global-graph-outer")
 | 
			
		||||
  container?.classList.add("active")
 | 
			
		||||
 | 
			
		||||
  function hideGlobalGraph(this: HTMLElement, e: HTMLElementEventMap["click"]) {
 | 
			
		||||
    if (e.target !== this) return
 | 
			
		||||
 | 
			
		||||
    container?.classList.remove("active")
 | 
			
		||||
    const graph = document.getElementById("global-graph-container")!
 | 
			
		||||
    removeAllChildren(graph)
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  container?.removeEventListener("click", hideGlobalGraph)
 | 
			
		||||
  container?.addEventListener("click", hideGlobalGraph)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
document.addEventListener("nav", async (e: unknown) => {
 | 
			
		||||
  const slug = (e as CustomEventMap["nav"]).detail.url
 | 
			
		||||
  await renderGraph("graph-container", slug)
 | 
			
		||||
 | 
			
		||||
  const containerIcon = document.getElementById("global-graph-icon")
 | 
			
		||||
  containerIcon?.removeEventListener("click", renderGlobalGraph)
 | 
			
		||||
  containerIcon?.addEventListener("click", renderGlobalGraph)
 | 
			
		||||
})
 | 
			
		||||
@@ -1,39 +1,50 @@
 | 
			
		||||
import { computePosition, inline, shift, autoPlacement } from "@floating-ui/dom"
 | 
			
		||||
import { computePosition, flip, inline, shift } from "@floating-ui/dom"
 | 
			
		||||
 | 
			
		||||
document.addEventListener("nav", () => {
 | 
			
		||||
  const links = [...document.getElementsByClassName("internal")] as HTMLLinkElement[]
 | 
			
		||||
  const p = new DOMParser()
 | 
			
		||||
  for (const link of links) {
 | 
			
		||||
    link.addEventListener("mouseenter", async ({ clientX, clientY }) => {
 | 
			
		||||
      if (link.dataset.fetchedPopover === "true") return
 | 
			
		||||
      async function setPosition(popoverElement: HTMLElement) {
 | 
			
		||||
        const { x, y } = await computePosition(link, popoverElement, {
 | 
			
		||||
          middleware: [inline({
 | 
			
		||||
            x: clientX,
 | 
			
		||||
            y: clientY
 | 
			
		||||
          }), shift(), flip()]
 | 
			
		||||
        })
 | 
			
		||||
        Object.assign(popoverElement.style, {
 | 
			
		||||
          left: `${x}px`,
 | 
			
		||||
          top: `${y}px`,
 | 
			
		||||
        })
 | 
			
		||||
      }
 | 
			
		||||
 | 
			
		||||
      if (link.dataset.fetchedPopover === "true") {
 | 
			
		||||
        return setPosition(link.lastChild as HTMLElement)
 | 
			
		||||
      }
 | 
			
		||||
 | 
			
		||||
      const url = link.href
 | 
			
		||||
      const anchor = new URL(url).hash
 | 
			
		||||
      if (anchor.startsWith("#")) return
 | 
			
		||||
 | 
			
		||||
      const contents = await fetch(`${url}`)
 | 
			
		||||
        .then((res) => res.text())
 | 
			
		||||
        .catch((err) => {
 | 
			
		||||
          console.error(err)
 | 
			
		||||
        })
 | 
			
		||||
 | 
			
		||||
      if (!contents) return
 | 
			
		||||
      const html = p.parseFromString(contents, "text/html")
 | 
			
		||||
      const elts = [...html.getElementsByClassName("popover-hint")]
 | 
			
		||||
      if (elts.length === 0) return
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
      const popoverElement = document.createElement("div")
 | 
			
		||||
      popoverElement.classList.add("popover")
 | 
			
		||||
      elts.forEach(elt => popoverElement.appendChild(elt))
 | 
			
		||||
 | 
			
		||||
      const { x, y } = await computePosition(link, popoverElement, {
 | 
			
		||||
        middleware: [inline({
 | 
			
		||||
          x: clientX,
 | 
			
		||||
          y: clientY
 | 
			
		||||
        }), shift(), autoPlacement()]
 | 
			
		||||
      })
 | 
			
		||||
 | 
			
		||||
      Object.assign(popoverElement.style, {
 | 
			
		||||
        left: `${x}px`,
 | 
			
		||||
        top: `${y}px`,
 | 
			
		||||
      })
 | 
			
		||||
      const popoverInner = document.createElement("div")
 | 
			
		||||
      popoverInner.classList.add("popover-inner")
 | 
			
		||||
      popoverElement.appendChild(popoverInner)
 | 
			
		||||
      elts.forEach(elt => popoverInner.appendChild(elt))
 | 
			
		||||
 | 
			
		||||
      setPosition(popoverElement)
 | 
			
		||||
      link.appendChild(popoverElement)
 | 
			
		||||
      link.dataset.fetchedPopover = "true"
 | 
			
		||||
    })
 | 
			
		||||
 
 | 
			
		||||
@@ -29,8 +29,8 @@ const getOpts = ({ target }: Event): { url: URL, scroll?: boolean } | undefined
 | 
			
		||||
  return { url: new URL(href), scroll: 'routerNoscroll' in a.dataset ? false : undefined }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
function notifyNav(slug: string) {
 | 
			
		||||
  const event = new CustomEvent("nav", { detail: { slug } })
 | 
			
		||||
function notifyNav(url: string) {
 | 
			
		||||
  const event: CustomEventMap["nav"] = new CustomEvent("nav", { detail: { url } })
 | 
			
		||||
  document.dispatchEvent(event)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@@ -73,6 +73,8 @@ async function navigate(url: URL, isBack: boolean = false) {
 | 
			
		||||
  delete announcer.dataset.persist
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
window.spaNavigate = navigate
 | 
			
		||||
 | 
			
		||||
function createRouter() {
 | 
			
		||||
  if (typeof window !== "undefined") {
 | 
			
		||||
    window.addEventListener("click", async (event) => {
 | 
			
		||||
 
 | 
			
		||||
@@ -1,8 +1,7 @@
 | 
			
		||||
.darkmode {
 | 
			
		||||
  float: right;
 | 
			
		||||
  padding: 1rem;
 | 
			
		||||
  min-width: 30px;
 | 
			
		||||
  position: relative;
 | 
			
		||||
  width: 20px;
 | 
			
		||||
  height: 20px;
 | 
			
		||||
 | 
			
		||||
  & > .toggle {
 | 
			
		||||
    display: none;
 | 
			
		||||
@@ -16,7 +15,6 @@
 | 
			
		||||
    width: 20px;
 | 
			
		||||
    height: 20px;
 | 
			
		||||
    top: calc(50% - 10px);
 | 
			
		||||
    margin: 0 7px;
 | 
			
		||||
    fill: var(--darkgray);
 | 
			
		||||
    transition: opacity 0.1s ease;
 | 
			
		||||
  }
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										68
									
								
								quartz/components/styles/graph.scss
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										68
									
								
								quartz/components/styles/graph.scss
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,68 @@
 | 
			
		||||
.graph {
 | 
			
		||||
  & > h3 {
 | 
			
		||||
    font-size: 1rem;
 | 
			
		||||
    margin: 0
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  & > .graph-outer {
 | 
			
		||||
    border-radius: 5px;
 | 
			
		||||
    border: 1px solid var(--lightgray);
 | 
			
		||||
    box-sizing: border-box;
 | 
			
		||||
    height: 250px;
 | 
			
		||||
    width: 300px;
 | 
			
		||||
    margin: 0.5em 0;
 | 
			
		||||
    position: relative;
 | 
			
		||||
 | 
			
		||||
    & > #global-graph-icon {
 | 
			
		||||
      color: var(--dark);
 | 
			
		||||
      opacity: 0.5;
 | 
			
		||||
      width: 18px;
 | 
			
		||||
      height: 18px;
 | 
			
		||||
      position: absolute;
 | 
			
		||||
      padding: 0.2rem;
 | 
			
		||||
      margin: 0.3rem;
 | 
			
		||||
      top: 0;
 | 
			
		||||
      right: 0;
 | 
			
		||||
      border-radius: 4px;
 | 
			
		||||
      background-color: transparent; 
 | 
			
		||||
      transition: background-color 0.5s ease;
 | 
			
		||||
      cursor: pointer;
 | 
			
		||||
      &:hover {
 | 
			
		||||
        background-color: var(--lightgray);
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    & > #graph-container > svg {
 | 
			
		||||
      margin-bottom: -5px;
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  & > #global-graph-outer {
 | 
			
		||||
    position: fixed;
 | 
			
		||||
    z-index: 9999;
 | 
			
		||||
    left: 0;
 | 
			
		||||
    top: 0;
 | 
			
		||||
    width: 100vw;
 | 
			
		||||
    height: 100%;
 | 
			
		||||
    overflow: scroll;
 | 
			
		||||
    backdrop-filter: blur(4px);
 | 
			
		||||
    display: none;
 | 
			
		||||
 | 
			
		||||
    &.active {
 | 
			
		||||
      display: inline-block;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    & > #global-graph-container {
 | 
			
		||||
      border: 1px solid var(--lightgray);
 | 
			
		||||
      background-color: var(--light); 
 | 
			
		||||
      border-radius: 5px;
 | 
			
		||||
      box-sizing: border-box;
 | 
			
		||||
      position: fixed;
 | 
			
		||||
      top: 50%;
 | 
			
		||||
      left: 50%;
 | 
			
		||||
      transform: translate(-50%, -50%);
 | 
			
		||||
      height: 60vh;
 | 
			
		||||
      width: 50vw;
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
@@ -3,7 +3,7 @@
 | 
			
		||||
    opacity: 0;
 | 
			
		||||
    visibility: hidden;
 | 
			
		||||
  }
 | 
			
		||||
  50% {
 | 
			
		||||
  1% {
 | 
			
		||||
    opacity: 0;
 | 
			
		||||
  }
 | 
			
		||||
  100% {
 | 
			
		||||
@@ -15,21 +15,24 @@
 | 
			
		||||
.popover {
 | 
			
		||||
  z-index: 999;
 | 
			
		||||
  position: absolute;
 | 
			
		||||
  overflow: scroll;
 | 
			
		||||
  overflow: visible;
 | 
			
		||||
  padding: 1rem;
 | 
			
		||||
 | 
			
		||||
  & > .popover-inner {
 | 
			
		||||
    width: 30rem;
 | 
			
		||||
    height: 20rem;
 | 
			
		||||
  padding: 0 1rem;
 | 
			
		||||
  margin-top: -1rem;
 | 
			
		||||
  border: 1px solid var(--lightgray);
 | 
			
		||||
    padding: 0 1rem 1rem 1rem;
 | 
			
		||||
    font-weight: initial;
 | 
			
		||||
    border: 1px solid var(--gray);
 | 
			
		||||
    background-color: var(--light);
 | 
			
		||||
    border-radius: 5px;
 | 
			
		||||
    box-shadow: 6px 6px 36px 0 rgba(0,0,0,0.25);
 | 
			
		||||
 | 
			
		||||
  font-weight: initial;
 | 
			
		||||
    overflow: scroll;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  visibility: hidden;
 | 
			
		||||
  opacity: 0;
 | 
			
		||||
  transition: opacity 0.2s ease, visibility 0.2s ease;
 | 
			
		||||
  transition: opacity 0.3s ease, visibility 0.3s ease;
 | 
			
		||||
 | 
			
		||||
  @media all and (max-width: 600px) {
 | 
			
		||||
    display: none !important;
 | 
			
		||||
@@ -37,7 +40,7 @@
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
a:hover .popover, .popover:hover {
 | 
			
		||||
  animation: dropin 0.5s ease;
 | 
			
		||||
  opacity: 1;
 | 
			
		||||
  visibility: visible;
 | 
			
		||||
  animation: dropin 0.3s ease;
 | 
			
		||||
  animation-fill-mode: forwards;
 | 
			
		||||
  animation-delay: 0.2s;
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -30,6 +30,7 @@ button#toc {
 | 
			
		||||
  overflow: hidden;
 | 
			
		||||
  max-height: none;
 | 
			
		||||
  transition: max-height 0.3s ease;
 | 
			
		||||
  font-size: 0.9rem;
 | 
			
		||||
 | 
			
		||||
  & ul {
 | 
			
		||||
    list-style: none;
 | 
			
		||||
@@ -38,7 +39,7 @@ button#toc {
 | 
			
		||||
    & > li > a {
 | 
			
		||||
      color: var(--dark);
 | 
			
		||||
      opacity: 0.35;
 | 
			
		||||
      transition: 0.5s ease opacity;
 | 
			
		||||
      transition: 0.5s ease opacity, 0.3s ease color;
 | 
			
		||||
      &.in-view {
 | 
			
		||||
        opacity: 0.75;
 | 
			
		||||
      }
 | 
			
		||||
 
 | 
			
		||||
@@ -13,10 +13,6 @@ export function trimPathSuffix(fp: string): string {
 | 
			
		||||
    cleanPath = cleanPath.slice(0, -"index".length)
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  if (cleanPath === "") {
 | 
			
		||||
    cleanPath = "./"
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  return cleanPath + anchor
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@@ -36,7 +32,7 @@ export function slugify(s: string): string {
 | 
			
		||||
export function resolveToRoot(slug: string): string {
 | 
			
		||||
  let fp = trimPathSuffix(slug)
 | 
			
		||||
 | 
			
		||||
  if (fp === "./") {
 | 
			
		||||
  if (fp === "") {
 | 
			
		||||
    return "."
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -14,19 +14,20 @@ const defaultOptions: Options = {
 | 
			
		||||
  indexExternalLinks: false,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
type ContentIndex = Map<string, {
 | 
			
		||||
export type ContentIndex = Map<string, ContentDetails> 
 | 
			
		||||
export type ContentDetails = {
 | 
			
		||||
  title: string,
 | 
			
		||||
  links?: string[],
 | 
			
		||||
  tags?: string[],
 | 
			
		||||
  content: string,
 | 
			
		||||
}> 
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export const ContentIndex: QuartzEmitterPlugin<Options> = (userOpts) => {
 | 
			
		||||
  const opts = { ...userOpts, ...defaultOptions }
 | 
			
		||||
  return {
 | 
			
		||||
    name: "ContentIndex",
 | 
			
		||||
    async emit(_contentDir, _cfg, content, _resources, emit) {
 | 
			
		||||
      const fp = "contentIndex"
 | 
			
		||||
      const fp = path.join("static", "contentIndex")
 | 
			
		||||
      const linkIndex: ContentIndex = new Map()
 | 
			
		||||
      for (const [tree, file] of content) {
 | 
			
		||||
        let slug = trimPathSuffix(file.data.slug!)
 | 
			
		||||
 
 | 
			
		||||
@@ -2,7 +2,7 @@ import { JSResourceToScriptElement, StaticResources } from "../../resources"
 | 
			
		||||
import { QuartzEmitterPlugin } from "../types"
 | 
			
		||||
import { render } from "preact-render-to-string"
 | 
			
		||||
import { QuartzComponent } from "../../components/types"
 | 
			
		||||
import { resolveToRoot } from "../../path"
 | 
			
		||||
import { resolveToRoot, trimPathSuffix } from "../../path"
 | 
			
		||||
import HeaderConstructor from "../../components/Header"
 | 
			
		||||
import { QuartzComponentProps } from "../../components/types"
 | 
			
		||||
import BodyConstructor from "../../components/Body"
 | 
			
		||||
@@ -56,7 +56,7 @@ export const ContentPage: QuartzEmitterPlugin<Options> = (opts) => {
 | 
			
		||||
        const Content = opts.content
 | 
			
		||||
        const doc = <html>
 | 
			
		||||
          <Head {...componentData} />
 | 
			
		||||
          <body data-slug={file.data.slug}>
 | 
			
		||||
          <body data-slug={trimPathSuffix(file.data.slug ?? "")}>
 | 
			
		||||
            <div id="quartz-root" class="page">
 | 
			
		||||
              <Header {...componentData} >
 | 
			
		||||
                {header.map(HeaderComponent => <HeaderComponent {...componentData} />)}
 | 
			
		||||
 
 | 
			
		||||
@@ -10,8 +10,6 @@ html {
 | 
			
		||||
 | 
			
		||||
body {
 | 
			
		||||
  margin: 0;
 | 
			
		||||
  height: 100vh;
 | 
			
		||||
  width: 100vw;
 | 
			
		||||
  max-width: 100%;
 | 
			
		||||
  box-sizing: border-box;
 | 
			
		||||
  background-color: var(--light);
 | 
			
		||||
@@ -48,31 +46,39 @@ a {
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.page {
 | 
			
		||||
  padding: 4rem 30vw;
 | 
			
		||||
  margin: 0 auto;
 | 
			
		||||
  margin: 6rem 35vw 6rem 20vw;
 | 
			
		||||
  max-width: 1000px;
 | 
			
		||||
  position: relative;
 | 
			
		||||
 | 
			
		||||
  & .left, & .right {
 | 
			
		||||
    position: fixed;
 | 
			
		||||
    padding: 0 4rem 0 6rem;
 | 
			
		||||
    max-width: 30vw;
 | 
			
		||||
    height: 100vh;
 | 
			
		||||
    overflow-y: scroll;
 | 
			
		||||
    box-sizing: border-box;
 | 
			
		||||
    top: 10rem;
 | 
			
		||||
    display: flex;
 | 
			
		||||
    flex-direction: column;
 | 
			
		||||
    top: 0;
 | 
			
		||||
    gap: 2rem;
 | 
			
		||||
    padding: 6rem;
 | 
			
		||||
  }
 | 
			
		||||
  
 | 
			
		||||
  & .left {
 | 
			
		||||
    left: 0;
 | 
			
		||||
    padding-left: 10vw;
 | 
			
		||||
    width: 20vw;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  & .right {
 | 
			
		||||
    right: 0;
 | 
			
		||||
    padding-right: 10vw;
 | 
			
		||||
    width: 35vw;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  @media all and (max-width: 1200px) {
 | 
			
		||||
    padding: 25px 5vw;
 | 
			
		||||
    margin: 25px 5vw;
 | 
			
		||||
    & .left, & .right {
 | 
			
		||||
      padding: 0;
 | 
			
		||||
      height: initial;
 | 
			
		||||
      max-width: none;
 | 
			
		||||
      position: initial;
 | 
			
		||||
    }
 | 
			
		||||
@@ -247,3 +253,7 @@ audio, video {
 | 
			
		||||
  width: 100%;
 | 
			
		||||
  border-radius: 5px;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.spacer {
 | 
			
		||||
  flex: 1 1 auto;
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user