add base structure
						commit
						c01138a81c
					
				| @ -0,0 +1,5 @@ | |||||||
|  | .DS_Store | ||||||
|  | public | ||||||
|  | resources | ||||||
|  | .idea | ||||||
|  | content/.obsidian | ||||||
| @ -0,0 +1,21 @@ | |||||||
|  | MIT License | ||||||
|  | 
 | ||||||
|  | Copyright (c) 2021 jackyzha0 | ||||||
|  | 
 | ||||||
|  | Permission is hereby granted, free of charge, to any person obtaining a copy | ||||||
|  | of this software and associated documentation files (the "Software"), to deal | ||||||
|  | in the Software without restriction, including without limitation the rights | ||||||
|  | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | ||||||
|  | copies of the Software, and to permit persons to whom the Software is | ||||||
|  | furnished to do so, subject to the following conditions: | ||||||
|  | 
 | ||||||
|  | The above copyright notice and this permission notice shall be included in all | ||||||
|  | copies or substantial portions of the Software. | ||||||
|  | 
 | ||||||
|  | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | ||||||
|  | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | ||||||
|  | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | ||||||
|  | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | ||||||
|  | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | ||||||
|  | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE | ||||||
|  | SOFTWARE. | ||||||
| @ -0,0 +1,10 @@ | |||||||
|  | # quartz | ||||||
|  | Simple second brain and digital garden. | ||||||
|  | 
 | ||||||
|  | ```shell | ||||||
|  | # Installation | ||||||
|  | go install github.com/jackyzha0/hugo-obsidian | ||||||
|  | 
 | ||||||
|  | # Run | ||||||
|  | hugo-obsidian -input=content -output=data | ||||||
|  | ``` | ||||||
| @ -0,0 +1,26 @@ | |||||||
|  | // Darkmode toggle
 | ||||||
|  | const toggleSwitch = document.querySelector('#darkmode-toggle') | ||||||
|  | 
 | ||||||
|  | const userPref = window.matchMedia('(prefers-color-scheme: light)').matches ? 'light' : 'dark' | ||||||
|  | const currentTheme = localStorage.getItem('theme') ?? userPref | ||||||
|  | 
 | ||||||
|  | if (currentTheme) { | ||||||
|  |   document.documentElement.setAttribute('saved-theme', currentTheme); | ||||||
|  |   if (currentTheme === 'dark') { | ||||||
|  |     toggleSwitch.checked = true | ||||||
|  |   } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | const switchTheme = (e) => { | ||||||
|  |   if (e.target.checked) { | ||||||
|  |     document.documentElement.setAttribute('saved-theme', 'dark') | ||||||
|  |     localStorage.setItem('theme', 'dark') | ||||||
|  |   } | ||||||
|  |   else { | ||||||
|  |     document.documentElement.setAttribute('saved-theme', 'light') | ||||||
|  |     localStorage.setItem('theme', 'light') | ||||||
|  |   } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | // listen for toggle
 | ||||||
|  | toggleSwitch.addEventListener('change', switchTheme, false) | ||||||
| @ -0,0 +1,67 @@ | |||||||
|  | 
 | ||||||
|  | .darkmode { | ||||||
|  |   text-align: right; | ||||||
|  | 
 | ||||||
|  |   & > .toggle { | ||||||
|  |     display: none; | ||||||
|  |     box-sizing: border-box; | ||||||
|  | 
 | ||||||
|  |     &:checked + .toggle-button:after { | ||||||
|  |       left: 50%; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     & + .toggle-button { | ||||||
|  |       box-sizing: border-box; | ||||||
|  |       outline: 0; | ||||||
|  |       display: inline-block; | ||||||
|  |       width: 3em; | ||||||
|  |       height: 1.5em; | ||||||
|  |       position: relative; | ||||||
|  |       cursor: pointer; | ||||||
|  |       border: 2px solid var(--gray); | ||||||
|  |       user-select: none; | ||||||
|  |       padding: 2px; | ||||||
|  |       transition: all 0.2s ease; | ||||||
|  |       border-radius: 2em; | ||||||
|  | 
 | ||||||
|  |       &:after, &:before { | ||||||
|  |         position: relative; | ||||||
|  |         display: block; | ||||||
|  |         box-sizing: border-box; | ||||||
|  |         content: ""; | ||||||
|  |         width: 50%; | ||||||
|  |         height: 100%; | ||||||
|  |       } | ||||||
|  | 
 | ||||||
|  |       &:before { | ||||||
|  |         display: none; | ||||||
|  |       } | ||||||
|  | 
 | ||||||
|  |       &:after { | ||||||
|  |         left: 0; | ||||||
|  |         transition: all 0.2s ease; | ||||||
|  |         background: var(--gray); | ||||||
|  |         content: ""; | ||||||
|  |         border-radius: 1em; | ||||||
|  |       } | ||||||
|  |     } | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   & #dayIcon { | ||||||
|  |     position: relative; | ||||||
|  |     width: 20px; | ||||||
|  |     height: 20px; | ||||||
|  |     top: -1.5px; | ||||||
|  |     margin: 0 7px; | ||||||
|  |     fill: var(--gray); | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   & #nightIcon { | ||||||
|  |     position: relative; | ||||||
|  |     width: 18px; | ||||||
|  |     height: 18px; | ||||||
|  |     top: -2px; | ||||||
|  |     margin: 0 7px; | ||||||
|  |     fill: var(--gray); | ||||||
|  |   } | ||||||
|  | } | ||||||
| @ -0,0 +1,99 @@ | |||||||
|  | /* Background */ .chroma { color: #f8f8f2; background-color: #282a36 } | ||||||
|  | /* Other */ .chroma .x {  } | ||||||
|  | /* Error */ .chroma .err {  } | ||||||
|  | /* LineTableTD */ .chroma .lntd { vertical-align: top; padding: 0; margin: 0; border: 0; } | ||||||
|  | /* LineTable */ .chroma .lntable { border-spacing: 0; padding: 0; margin: 0; border: 0; width: auto; overflow: auto; display: block; } | ||||||
|  | /* LineHighlight */ .chroma .hl { display: block; width: 100%;background-color: #ffffcc } | ||||||
|  | /* LineNumbersTable */ .chroma .lnt { margin-right: 0.4em; padding: 0 0.4em 0 0.4em;color: #7f7f7f } | ||||||
|  | /* LineNumbers */ .chroma .ln { margin-right: 0.4em; padding: 0 0.4em 0 0.4em;color: #7f7f7f } | ||||||
|  | /* Keyword */ .chroma .k { color: #ff79c6 } | ||||||
|  | /* KeywordConstant */ .chroma .kc { color: #ff79c6 } | ||||||
|  | /* KeywordDeclaration */ .chroma .kd { color: #8be9fd; font-style: italic } | ||||||
|  | /* KeywordNamespace */ .chroma .kn { color: #ff79c6 } | ||||||
|  | /* KeywordPseudo */ .chroma .kp { color: #ff79c6 } | ||||||
|  | /* KeywordReserved */ .chroma .kr { color: #ff79c6 } | ||||||
|  | /* KeywordType */ .chroma .kt { color: #8be9fd } | ||||||
|  | /* Name */ .chroma .n {  } | ||||||
|  | /* NameAttribute */ .chroma .na { color: #50fa7b } | ||||||
|  | /* NameBuiltin */ .chroma .nb { color: #8be9fd; font-style: italic } | ||||||
|  | /* NameBuiltinPseudo */ .chroma .bp {  } | ||||||
|  | /* NameClass */ .chroma .nc { color: #50fa7b } | ||||||
|  | /* NameConstant */ .chroma .no {  } | ||||||
|  | /* NameDecorator */ .chroma .nd {  } | ||||||
|  | /* NameEntity */ .chroma .ni {  } | ||||||
|  | /* NameException */ .chroma .ne {  } | ||||||
|  | /* NameFunction */ .chroma .nf { color: #50fa7b } | ||||||
|  | /* NameFunctionMagic */ .chroma .fm {  } | ||||||
|  | /* NameLabel */ .chroma .nl { color: #8be9fd; font-style: italic } | ||||||
|  | /* NameNamespace */ .chroma .nn {  } | ||||||
|  | /* NameOther */ .chroma .nx {  } | ||||||
|  | /* NameProperty */ .chroma .py {  } | ||||||
|  | /* NameTag */ .chroma .nt { color: #ff79c6 } | ||||||
|  | /* NameVariable */ .chroma .nv { color: #8be9fd; font-style: italic } | ||||||
|  | /* NameVariableClass */ .chroma .vc { color: #8be9fd; font-style: italic } | ||||||
|  | /* NameVariableGlobal */ .chroma .vg { color: #8be9fd; font-style: italic } | ||||||
|  | /* NameVariableInstance */ .chroma .vi { color: #8be9fd; font-style: italic } | ||||||
|  | /* NameVariableMagic */ .chroma .vm {  } | ||||||
|  | /* Literal */ .chroma .l {  } | ||||||
|  | /* LiteralDate */ .chroma .ld {  } | ||||||
|  | /* LiteralString */ .chroma .s { color: #f1fa8c } | ||||||
|  | /* LiteralStringAffix */ .chroma .sa { color: #f1fa8c } | ||||||
|  | /* LiteralStringBacktick */ .chroma .sb { color: #f1fa8c } | ||||||
|  | /* LiteralStringChar */ .chroma .sc { color: #f1fa8c } | ||||||
|  | /* LiteralStringDelimiter */ .chroma .dl { color: #f1fa8c } | ||||||
|  | /* LiteralStringDoc */ .chroma .sd { color: #f1fa8c } | ||||||
|  | /* LiteralStringDouble */ .chroma .s2 { color: #f1fa8c } | ||||||
|  | /* LiteralStringEscape */ .chroma .se { color: #f1fa8c } | ||||||
|  | /* LiteralStringHeredoc */ .chroma .sh { color: #f1fa8c } | ||||||
|  | /* LiteralStringInterpol */ .chroma .si { color: #f1fa8c } | ||||||
|  | /* LiteralStringOther */ .chroma .sx { color: #f1fa8c } | ||||||
|  | /* LiteralStringRegex */ .chroma .sr { color: #f1fa8c } | ||||||
|  | /* LiteralStringSingle */ .chroma .s1 { color: #f1fa8c } | ||||||
|  | /* LiteralStringSymbol */ .chroma .ss { color: #f1fa8c } | ||||||
|  | /* LiteralNumber */ .chroma .m { color: #bd93f9 } | ||||||
|  | /* LiteralNumberBin */ .chroma .mb { color: #bd93f9 } | ||||||
|  | /* LiteralNumberFloat */ .chroma .mf { color: #bd93f9 } | ||||||
|  | /* LiteralNumberHex */ .chroma .mh { color: #bd93f9 } | ||||||
|  | /* LiteralNumberInteger */ .chroma .mi { color: #bd93f9 } | ||||||
|  | /* LiteralNumberIntegerLong */ .chroma .il { color: #bd93f9 } | ||||||
|  | /* LiteralNumberOct */ .chroma .mo { color: #bd93f9 } | ||||||
|  | /* Operator */ .chroma .o { color: #ff79c6 } | ||||||
|  | /* OperatorWord */ .chroma .ow { color: #ff79c6 } | ||||||
|  | /* Punctuation */ .chroma .p {  } | ||||||
|  | /* Comment */ .chroma .c { color: #6272a4 } | ||||||
|  | /* CommentHashbang */ .chroma .ch { color: #6272a4 } | ||||||
|  | /* CommentMultiline */ .chroma .cm { color: #6272a4 } | ||||||
|  | /* CommentSingle */ .chroma .c1 { color: #6272a4 } | ||||||
|  | /* CommentSpecial */ .chroma .cs { color: #6272a4 } | ||||||
|  | /* CommentPreproc */ .chroma .cp { color: #ff79c6 } | ||||||
|  | /* CommentPreprocFile */ .chroma .cpf { color: #ff79c6 } | ||||||
|  | /* Generic */ .chroma .g {  } | ||||||
|  | /* GenericDeleted */ .chroma .gd { color: #8b080b } | ||||||
|  | /* GenericEmph */ .chroma .ge { text-decoration: underline } | ||||||
|  | /* GenericError */ .chroma .gr {  } | ||||||
|  | /* GenericHeading */ .chroma .gh { font-weight: bold } | ||||||
|  | /* GenericInserted */ .chroma .gi { font-weight: bold } | ||||||
|  | /* GenericOutput */ .chroma .go { color: #44475a } | ||||||
|  | /* GenericPrompt */ .chroma .gp {  } | ||||||
|  | /* GenericStrong */ .chroma .gs {  } | ||||||
|  | /* GenericSubheading */ .chroma .gu { font-weight: bold } | ||||||
|  | /* GenericTraceback */ .chroma .gt {  } | ||||||
|  | /* GenericUnderline */ .chroma .gl { text-decoration: underline } | ||||||
|  | /* TextWhitespace */ .chroma .w {  } | ||||||
|  | 
 | ||||||
|  | .lntd:first-of-type > .chroma { | ||||||
|  |   padding-right: 0; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | .chroma code { | ||||||
|  |   font-family: 'Fira Code' !important; | ||||||
|  |   font-size: 0.85em; | ||||||
|  |   line-height: 1em; | ||||||
|  |   background: none; | ||||||
|  |   padding: 0; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | .chroma { | ||||||
|  |   border-radius: 3px; | ||||||
|  |   margin: 0; | ||||||
|  | } | ||||||
| @ -0,0 +1,4 @@ | |||||||
|  | baseURL = "https://quartz.jzhao.xyz/" | ||||||
|  | languageCode = "en-us" | ||||||
|  | googleAnalytics = "UA-148413215-1" | ||||||
|  | pygmentsUseClasses = true | ||||||
| @ -0,0 +1,11 @@ | |||||||
|  | name: Quartz Example Page | ||||||
|  | description: | ||||||
|  |   Here is the page description. This is an example Quartz site that details installation, | ||||||
|  |   setup, customization, and troubleshooting for Quartz itself. | ||||||
|  | page_title: | ||||||
|  |   Quartz Example Page | ||||||
|  | links: | ||||||
|  |   - link_name: twitter | ||||||
|  |     link: https://twitter.com/_jzhao | ||||||
|  |   - link_name: github | ||||||
|  |     link: https://github.com/jackyzha0 | ||||||
| @ -0,0 +1,11 @@ | |||||||
|  | enableLegend: false | ||||||
|  | enableDrag: true | ||||||
|  | enableZoom: false | ||||||
|  | base: | ||||||
|  |   node: "#284b63" | ||||||
|  |   activeNode: "#f28482" | ||||||
|  |   inactiveNode: "#a8b3bd" | ||||||
|  |   link: "#babdbf" | ||||||
|  |   activeLink: "#5a7282" | ||||||
|  | paths: | ||||||
|  |   - /moc: "#4388cc" | ||||||
| @ -0,0 +1,10 @@ | |||||||
|  | <!DOCTYPE html> | ||||||
|  | <html lang="en"> | ||||||
|  | {{ block "head" . }} | ||||||
|  | {{ end }} | ||||||
|  | 
 | ||||||
|  | <body> | ||||||
|  | {{ block "main" . }} | ||||||
|  | {{ end }} | ||||||
|  | </body> | ||||||
|  | </html> | ||||||
| @ -0,0 +1,24 @@ | |||||||
|  | <!DOCTYPE html> | ||||||
|  | <html lang="en"> | ||||||
|  | {{ partial "head.html" . }} | ||||||
|  | 
 | ||||||
|  | <body> | ||||||
|  | <div class="singlePage"> | ||||||
|  |     <!-- Begin actual content --> | ||||||
|  |     {{partial "darkmode.html" .}} | ||||||
|  |     <article> | ||||||
|  |         {{if .Title}}<h1>{{ .Title }}</h1>{{end}} | ||||||
|  |         {{- .Content -}} | ||||||
|  |     </article> | ||||||
|  |     {{partial "footer.html" .}} | ||||||
|  | </div> | ||||||
|  | 
 | ||||||
|  | {{- with resources.Get "darkmode.js" | minify -}} | ||||||
|  | <script> | ||||||
|  |   {{.Content | safeJS }} | ||||||
|  | </script> | ||||||
|  | {{- end -}} | ||||||
|  | 
 | ||||||
|  | </body> | ||||||
|  | 
 | ||||||
|  | </html> | ||||||
| @ -0,0 +1,9 @@ | |||||||
|  | <ol class="backlinks"> | ||||||
|  |     {{$curPage := strings.TrimRight "/" .Page.RelPermalink }} | ||||||
|  |     {{$inbound := index $.Site.Data.linkIndex.index.backlinks $curPage}} | ||||||
|  |     {{- range $inbound -}} | ||||||
|  |     <li> | ||||||
|  |         <a href="{{index . "source"}}">{{index . "source"}}</a> | ||||||
|  |     </li> | ||||||
|  |     {{- end -}} | ||||||
|  | </ol> | ||||||
| @ -0,0 +1,16 @@ | |||||||
|  | <div class='darkmode'> | ||||||
|  |     <label id="toggle-label-light" for='darkmode-toggle' tabindex="-1"> | ||||||
|  |         <svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" version="1.1" id="dayIcon" x="0px" y="0px" viewBox="0 0 35 35" style="enable-background:new 0 0 35 35;" xml:space="preserve"> | ||||||
|  |             <title>Light Mode</title> | ||||||
|  |             <path d="M6,17.5C6,16.672,5.328,16,4.5,16h-3C0.672,16,0,16.672,0,17.5    S0.672,19,1.5,19h3C5.328,19,6,18.328,6,17.5z M7.5,26c-0.414,0-0.789,0.168-1.061,0.439l-2,2C4.168,28.711,4,29.086,4,29.5    C4,30.328,4.671,31,5.5,31c0.414,0,0.789-0.168,1.06-0.44l2-2C8.832,28.289,9,27.914,9,27.5C9,26.672,8.329,26,7.5,26z M17.5,6    C18.329,6,19,5.328,19,4.5v-3C19,0.672,18.329,0,17.5,0S16,0.672,16,1.5v3C16,5.328,16.671,6,17.5,6z M27.5,9    c0.414,0,0.789-0.168,1.06-0.439l2-2C30.832,6.289,31,5.914,31,5.5C31,4.672,30.329,4,29.5,4c-0.414,0-0.789,0.168-1.061,0.44    l-2,2C26.168,6.711,26,7.086,26,7.5C26,8.328,26.671,9,27.5,9z M6.439,8.561C6.711,8.832,7.086,9,7.5,9C8.328,9,9,8.328,9,7.5    c0-0.414-0.168-0.789-0.439-1.061l-2-2C6.289,4.168,5.914,4,5.5,4C4.672,4,4,4.672,4,5.5c0,0.414,0.168,0.789,0.439,1.06    L6.439,8.561z M33.5,16h-3c-0.828,0-1.5,0.672-1.5,1.5s0.672,1.5,1.5,1.5h3c0.828,0,1.5-0.672,1.5-1.5S34.328,16,33.5,16z     M28.561,26.439C28.289,26.168,27.914,26,27.5,26c-0.828,0-1.5,0.672-1.5,1.5c0,0.414,0.168,0.789,0.439,1.06l2,2    C28.711,30.832,29.086,31,29.5,31c0.828,0,1.5-0.672,1.5-1.5c0-0.414-0.168-0.789-0.439-1.061L28.561,26.439z M17.5,29    c-0.829,0-1.5,0.672-1.5,1.5v3c0,0.828,0.671,1.5,1.5,1.5s1.5-0.672,1.5-1.5v-3C19,29.672,18.329,29,17.5,29z M17.5,7    C11.71,7,7,11.71,7,17.5S11.71,28,17.5,28S28,23.29,28,17.5S23.29,7,17.5,7z M17.5,25c-4.136,0-7.5-3.364-7.5-7.5    c0-4.136,3.364-7.5,7.5-7.5c4.136,0,7.5,3.364,7.5,7.5C25,21.636,21.636,25,17.5,25z" /> | ||||||
|  |         </svg> | ||||||
|  |     </label> | ||||||
|  |     <input class='toggle' id='darkmode-toggle' type='checkbox' tabindex="-1"> | ||||||
|  |     <label class='toggle-button' for='darkmode-toggle'></label> | ||||||
|  |     <label id="toggle-label-dark" for='darkmode-toggle' tabindex="-1"> | ||||||
|  |         <svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" version="1.1" id="nightIcon" x="0px" y="0px" viewBox="0 0 100 100" style="enable-background='new 0 0 100 100'" xml:space="preserve"> | ||||||
|  |             <title>Dark Mode</title> | ||||||
|  |             <path d="M96.76,66.458c-0.853-0.852-2.15-1.064-3.23-0.534c-6.063,2.991-12.858,4.571-19.655,4.571  C62.022,70.495,50.88,65.88,42.5,57.5C29.043,44.043,25.658,23.536,34.076,6.47c0.532-1.08,0.318-2.379-0.534-3.23  c-0.851-0.852-2.15-1.064-3.23-0.534c-4.918,2.427-9.375,5.619-13.246,9.491c-9.447,9.447-14.65,22.008-14.65,35.369  c0,13.36,5.203,25.921,14.65,35.368s22.008,14.65,35.368,14.65c13.361,0,25.921-5.203,35.369-14.65  c3.872-3.871,7.064-8.328,9.491-13.246C97.826,68.608,97.611,67.309,96.76,66.458z" /> | ||||||
|  |         </svg> | ||||||
|  |     </label> | ||||||
|  | </div> | ||||||
| @ -0,0 +1,21 @@ | |||||||
|  | <div> | ||||||
|  |     <hr/> | ||||||
|  |     {{partial "graph.html" .}} | ||||||
|  |     <ul id="sub-nav"> | ||||||
|  |         <li><a href="/">↳ Take me home</a></li> | ||||||
|  |     </ul> | ||||||
|  | </div> | ||||||
|  | 
 | ||||||
|  | <!-- Contact Info --> | ||||||
|  | <div id="contact_buttons" class="lt-centre"> | ||||||
|  |     <footer> | ||||||
|  |         <p>made by {{ $.Site.Data.config.name }}, © {{ dateFormat "2006" now }}</p> | ||||||
|  |         <a href="https://github.com/jackyzha0/quartz">source</a> | ||||||
|  |         {{ if not .IsHome }} | ||||||
|  |         <a href="/">home</a> | ||||||
|  |         {{end}} | ||||||
|  |         {{- range $.Site.Data.links.footer -}} | ||||||
|  |         <a href="{{.link}}">{{.link_name}}</a> | ||||||
|  |         {{- end -}} | ||||||
|  |     </footer> | ||||||
|  | </div> | ||||||
| @ -0,0 +1,218 @@ | |||||||
|  | <script src="https://cdn.jsdelivr.net/npm/d3@6"></script> | ||||||
|  | <div id="graph-container"></div> | ||||||
|  | <style> | ||||||
|  |     :root { | ||||||
|  |         --g-node: {{.Site.Data.graphConfig.base.node}}; | ||||||
|  |         --g-node-active: {{.Site.Data.graphConfig.base.activeNode}}; | ||||||
|  |         --g-node-inactive: {{.Site.Data.graphConfig.base.inactiveNode}}; | ||||||
|  |         --g-link: {{.Site.Data.graphConfig.base.link}}; | ||||||
|  |         --g-link-active: {{.Site.Data.graphConfig.base.activeLink}}; | ||||||
|  |     } | ||||||
|  | </style> | ||||||
|  | <script> | ||||||
|  |   const index = {{$.Site.Data.linkIndex.index}} | ||||||
|  |   const links = {{$.Site.Data.linkIndex.links}} | ||||||
|  |   const curPage = {{ strings.TrimRight "/" .Page.RelPermalink }} | ||||||
|  |   const pathColors = {{$.Site.Data.graphConfig.paths}} | ||||||
|  | 
 | ||||||
|  |   const parseIdsFromLinks = (links) => [...(new Set(links.flatMap(link => ([link.source, link.target]))))] | ||||||
|  | 
 | ||||||
|  |   const data = { | ||||||
|  |     nodes: parseIdsFromLinks(links).map(id => ({id})), | ||||||
|  |     links, | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   const color = (d) => { | ||||||
|  |     if (d.id === curPage) { | ||||||
|  |       return "var(--g-node-active)" | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     for (const pathColor of pathColors) { | ||||||
|  |       const path = Object.keys(pathColor)[0] | ||||||
|  |       const colour = pathColor[path] | ||||||
|  |       if (d.id.startsWith(path)) { | ||||||
|  |         return colour | ||||||
|  |       } | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     return "var(--g-node)" | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   const drag = simulation => { | ||||||
|  |     function dragstarted(event, d) { | ||||||
|  |       if (!event.active) simulation.alphaTarget(1).restart(); | ||||||
|  |       d.fx = d.x; | ||||||
|  |       d.fy = d.y; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     function dragged(event,d) { | ||||||
|  |       d.fx = event.x; | ||||||
|  |       d.fy = event.y; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     function dragended(event,d) { | ||||||
|  |       if (!event.active) simulation.alphaTarget(0); | ||||||
|  |       d.fx = null; | ||||||
|  |       d.fy = null; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     const enableDrag = {{$.Site.Data.graphConfig.enableDrag}} | ||||||
|  |     const noop = () => {} | ||||||
|  |     return d3.drag() | ||||||
|  |       .on("start", enableDrag ? dragstarted : noop) | ||||||
|  |       .on("drag", enableDrag ? dragged : noop) | ||||||
|  |       .on("end", enableDrag ? dragended : noop); | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   const height = 400 | ||||||
|  |   const width = document.getElementById("graph-container").offsetWidth | ||||||
|  | 
 | ||||||
|  |   const simulation = d3.forceSimulation(data.nodes) | ||||||
|  |     .force("charge", d3.forceManyBody().strength(-20)) | ||||||
|  |     .force("link", d3.forceLink(data.links) | ||||||
|  |       .id(d => d.id) | ||||||
|  |     ) | ||||||
|  |     .force("center", d3.forceCenter()); | ||||||
|  | 
 | ||||||
|  |   const svg = d3.select('#graph-container') | ||||||
|  |     .append('svg') | ||||||
|  |     .attr('width', width) | ||||||
|  |     .attr('height', height) | ||||||
|  |     .attr("viewBox", [-width / 2, -height / 2, width, height]); | ||||||
|  | 
 | ||||||
|  |   // legend | ||||||
|  |   const enableLegend = {{$.Site.Data.graphConfig.enableLegend}} | ||||||
|  |   if (enableLegend) { | ||||||
|  |     const legend = [ | ||||||
|  |       {"Current": "var(--g-node-active)"}, | ||||||
|  |       {"Note": "var(--g-node)"}, | ||||||
|  |       ...pathColors | ||||||
|  |     ] | ||||||
|  |     legend.forEach((legendEntry, i) => { | ||||||
|  |       const key = Object.keys(legendEntry)[0] | ||||||
|  |       const colour = legendEntry[key] | ||||||
|  |       svg.append("circle").attr("cx", -width/2 + 20).attr("cy", height/2 - 30 * (i+1)).attr("r", 6).style("fill", colour) | ||||||
|  |       svg.append("text").attr("x", -width/2 + 40).attr("y", height/2 - 30 * (i+1)).text(key).style("font-size", "15px").attr("alignment-baseline","middle") | ||||||
|  |     }) | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   // draw links between nodes | ||||||
|  |   const link = svg.append("g") | ||||||
|  |     .selectAll("line") | ||||||
|  |     .data(data.links) | ||||||
|  |     .join("line") | ||||||
|  |     .attr("class", "link") | ||||||
|  |     .attr("stroke", "var(--g-link)") | ||||||
|  |     .attr("stroke-width", 2) | ||||||
|  |     .attr("data-source", d => d.source.id) | ||||||
|  |     .attr("data-target", d => d.target.id) | ||||||
|  | 
 | ||||||
|  |   // svg groups | ||||||
|  |   const graphNode = svg.append("g") | ||||||
|  |     .selectAll("g") | ||||||
|  |     .data(data.nodes) | ||||||
|  |     .enter().append("g") | ||||||
|  | 
 | ||||||
|  |   // draw individual nodes | ||||||
|  |   const node = graphNode.append("circle") | ||||||
|  |     .attr("class", "node") | ||||||
|  |     .attr("id", (d) => d.id) | ||||||
|  |     .attr("r", (d) => { | ||||||
|  |       const numOut = index.links[d.id]?.length || 0 | ||||||
|  |       const numIn = index.backlinks[d.id]?.length || 0 | ||||||
|  |       return 3 + (numOut + numIn) / 4 | ||||||
|  |     }) | ||||||
|  |     .attr("fill", color) | ||||||
|  |     .style("cursor", "pointer") | ||||||
|  |     .on("click", (_, d) => { | ||||||
|  |       window.location.href = d.id; | ||||||
|  |     }) | ||||||
|  |     .on("mouseover", function (_, d) { | ||||||
|  |       d3.selectAll(".node") | ||||||
|  |         .transition() | ||||||
|  |         .duration(100) | ||||||
|  |         .attr("fill", "var(--g-node-inactive)") | ||||||
|  | 
 | ||||||
|  |       const neighbours = parseIdsFromLinks([...(index.links[d.id] || []), ...(index.backlinks[d.id] || [])]) | ||||||
|  |       const neighbourNodes = d3.selectAll(".node").filter(d => neighbours.includes(d.id)) | ||||||
|  |       const currentId = d.id | ||||||
|  |       const links = d3.selectAll(".link").filter(d => d.source.id === currentId || d.target.id === currentId) | ||||||
|  | 
 | ||||||
|  |       // highlight neighbour nodes | ||||||
|  |       neighbourNodes | ||||||
|  |         .transition() | ||||||
|  |         .duration(200) | ||||||
|  |         .attr("fill", color) | ||||||
|  | 
 | ||||||
|  |       // highlight links | ||||||
|  |       links | ||||||
|  |         .transition() | ||||||
|  |         .duration(200) | ||||||
|  |         .attr("stroke", "var(--g-link-active)") | ||||||
|  | 
 | ||||||
|  |       // show text for self | ||||||
|  |       d3.select(this.parentNode) | ||||||
|  |         .select("text") | ||||||
|  |         .raise() | ||||||
|  |         .transition() | ||||||
|  |         .duration(200) | ||||||
|  |         .style("opacity", 1) | ||||||
|  |     }).on("mouseleave", function (_,d) { | ||||||
|  |       d3.selectAll(".node") | ||||||
|  |         .transition() | ||||||
|  |         .duration(200) | ||||||
|  |         .attr("fill", color) | ||||||
|  | 
 | ||||||
|  |       const currentId = d.id | ||||||
|  |       const links = d3.selectAll(".link").filter(d => d.source.id === currentId || d.target.id === currentId) | ||||||
|  | 
 | ||||||
|  |       links | ||||||
|  |         .transition() | ||||||
|  |         .duration(200) | ||||||
|  |         .attr("stroke", "var(--g-link)") | ||||||
|  | 
 | ||||||
|  |       d3.select(this.parentNode) | ||||||
|  |         .select("text") | ||||||
|  |         .transition() | ||||||
|  |         .duration(200) | ||||||
|  |         .style("opacity", 0) | ||||||
|  |     }) | ||||||
|  |     .call(drag(simulation)); | ||||||
|  | 
 | ||||||
|  |   // draw labels | ||||||
|  |   const labels = graphNode.append("text") | ||||||
|  |     .attr("dx", 12) | ||||||
|  |     .attr("dy", ".35em") | ||||||
|  |     .text((d) => d.id) | ||||||
|  |     .style("opacity", 0) | ||||||
|  |     .style("pointer-events", "none") | ||||||
|  |     .call(drag(simulation)); | ||||||
|  | 
 | ||||||
|  |   // set panning | ||||||
|  |   const enableZoom = {{$.Site.Data.graphConfig.enableZoom}} | ||||||
|  |   if (enableZoom) { | ||||||
|  |     svg.call(d3.zoom() | ||||||
|  |       .extent([[0, 0], [width, height]]) | ||||||
|  |       .scaleExtent([0.25, 4]) | ||||||
|  |       .on("zoom", ({transform}) => { | ||||||
|  |         link.attr("transform", transform); | ||||||
|  |         node.attr("transform", transform); | ||||||
|  |         labels.attr("transform", transform); | ||||||
|  |       })); | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   // progress the simulation | ||||||
|  |   simulation.on("tick", () => { | ||||||
|  |     link | ||||||
|  |       .attr("x1", d => d.source.x) | ||||||
|  |       .attr("y1", d => d.source.y) | ||||||
|  |       .attr("x2", d => d.target.x) | ||||||
|  |       .attr("y2", d => d.target.y); | ||||||
|  |     node | ||||||
|  |       .attr("cx", d => d.x) | ||||||
|  |       .attr("cy", d => d.y); | ||||||
|  |     labels | ||||||
|  |       .attr("x", d => d.x) | ||||||
|  |       .attr("y", d => d.y); | ||||||
|  |   }); | ||||||
|  | </script> | ||||||
| @ -0,0 +1,24 @@ | |||||||
|  | <head> | ||||||
|  |     <link rel="preconnect" href="https://www.googletagmanager.com"> | ||||||
|  |     <link crossorigin rel="preconnect" href="https://www.google-analytics.com"> | ||||||
|  |     {{ template "_internal/google_analytics_async.html" . }} | ||||||
|  | 
 | ||||||
|  |     <!-- Meta tags --> | ||||||
|  |     <meta charset="UTF-8"> | ||||||
|  |     <meta name="description" content="{{$.Site.Data.config.description}}"> | ||||||
|  |     <title>{{$.Site.Data.config.page_title}}</title> | ||||||
|  |     <meta name="viewport" content="width=device-width, initial-scale=1"> | ||||||
|  |     <link rel="shortcut icon" type="image/png" href="/icon.png" /> | ||||||
|  | 
 | ||||||
|  |     <!-- CSS Stylesheets and Fonts --> | ||||||
|  |     <link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;700&family=Source+Sans+Pro:wght@400;700&family=Fira+Code:wght@400;700&display=swap" rel="stylesheet"> | ||||||
|  |     {{ $css := slice "darkmode.scss" "syntax.scss"}} | ||||||
|  |     {{range $css}} | ||||||
|  |     {{$sass := resources.Get . | resources.ToCSS }} | ||||||
|  |     {{with $sass | minify}} | ||||||
|  |     <style> | ||||||
|  |         {{.Content | safeCSS}} | ||||||
|  |     </style> | ||||||
|  |     {{end}} | ||||||
|  |     {{end}} | ||||||
|  | </head> | ||||||
											
												Binary file not shown.
											
										
									
								| After Width: | Height: | Size: 31 KiB | 
					Loading…
					
					
				
		Reference in New Issue