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