add base structure
This commit is contained in:
commit
c01138a81c
5
.gitignore
vendored
Normal file
5
.gitignore
vendored
Normal file
@ -0,0 +1,5 @@
|
||||
.DS_Store
|
||||
public
|
||||
resources
|
||||
.idea
|
||||
content/.obsidian
|
21
LICENSE.txt
Normal file
21
LICENSE.txt
Normal file
@ -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.
|
10
README.md
Normal file
10
README.md
Normal file
@ -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
|
||||
```
|
26
assets/darkmode.js
Normal file
26
assets/darkmode.js
Normal file
@ -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)
|
67
assets/darkmode.scss
Normal file
67
assets/darkmode.scss
Normal file
@ -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);
|
||||
}
|
||||
}
|
99
assets/syntax.scss
Normal file
99
assets/syntax.scss
Normal file
@ -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;
|
||||
}
|
4
config.toml
Normal file
4
config.toml
Normal file
@ -0,0 +1,4 @@
|
||||
baseURL = "https://quartz.jzhao.xyz/"
|
||||
languageCode = "en-us"
|
||||
googleAnalytics = "UA-148413215-1"
|
||||
pygmentsUseClasses = true
|
0
content/_index.md
Normal file
0
content/_index.md
Normal file
0
content/moc/directory.md
Normal file
0
content/moc/directory.md
Normal file
0
content/notes/config.md
Normal file
0
content/notes/config.md
Normal file
0
content/notes/setup.md
Normal file
0
content/notes/setup.md
Normal file
0
content/notes/troubleshooting.md
Normal file
0
content/notes/troubleshooting.md
Normal file
0
content/notes/welcome.md
Normal file
0
content/notes/welcome.md
Normal file
11
data/config.yaml
Normal file
11
data/config.yaml
Normal file
@ -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
|
11
data/graphConfig.yaml
Normal file
11
data/graphConfig.yaml
Normal file
@ -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
layouts/404.html
Normal file
0
layouts/404.html
Normal file
10
layouts/_default/baseof.html
Normal file
10
layouts/_default/baseof.html
Normal file
@ -0,0 +1,10 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
{{ block "head" . }}
|
||||
{{ end }}
|
||||
|
||||
<body>
|
||||
{{ block "main" . }}
|
||||
{{ end }}
|
||||
</body>
|
||||
</html>
|
24
layouts/_default/single.html
Normal file
24
layouts/_default/single.html
Normal file
@ -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
layouts/index.html
Normal file
0
layouts/index.html
Normal file
9
layouts/partials/backlinks.html
Normal file
9
layouts/partials/backlinks.html
Normal file
@ -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>
|
16
layouts/partials/darkmode.html
Normal file
16
layouts/partials/darkmode.html
Normal file
@ -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>
|
21
layouts/partials/footer.html
Normal file
21
layouts/partials/footer.html
Normal file
@ -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>
|
218
layouts/partials/graph.html
Normal file
218
layouts/partials/graph.html
Normal file
@ -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>
|
24
layouts/partials/head.html
Normal file
24
layouts/partials/head.html
Normal file
@ -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>
|
0
layouts/partials/header.html
Normal file
0
layouts/partials/header.html
Normal file
BIN
static/icon.png
Normal file
BIN
static/icon.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 31 KiB |
Loading…
Reference in New Issue
Block a user