summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--LICENSE13
-rw-r--r--README.md22
-rw-r--r--css/cursor.css3
-rw-r--r--css/default.css2
-rw-r--r--css/kate.css66
-rw-r--r--css/style.css1155
-rw-r--r--images/art-tampere.jpgbin0 -> 123795 bytes
-rw-r--r--images/button/email.gifbin0 -> 5009 bytes
-rw-r--r--images/button/haskell.gifbin0 -> 4842 bytes
-rw-r--r--images/button/paws.gifbin0 -> 877 bytes
-rw-r--r--images/button/under-construction.gifbin0 -> 1881 bytes
-rw-r--r--images/colour-beach-hue.jpgbin0 -> 26351 bytes
-rw-r--r--images/colour-beach-monochrome.jpgbin0 -> 18051 bytes
-rw-r--r--images/colour-krita.webpbin0 -> 12684 bytes
-rw-r--r--images/colour-saturation.pngbin0 -> 42294 bytes
-rw-r--r--images/cursor/AppStarting.anibin0 -> 34500 bytes
-rw-r--r--images/cursor/Help.curbin0 -> 7598 bytes
-rw-r--r--images/cursor/IBeam.curbin0 -> 7598 bytes
-rw-r--r--images/cursor/No.curbin0 -> 7598 bytes
-rw-r--r--images/cursor/SizeAll.curbin0 -> 4286 bytes
-rw-r--r--images/cursor/SizeNESW.curbin0 -> 4286 bytes
-rw-r--r--images/cursor/SizeNS.curbin0 -> 4286 bytes
-rw-r--r--images/cursor/SizeNWSE.curbin0 -> 4286 bytes
-rw-r--r--images/cursor/SizeWE.curbin0 -> 4286 bytes
-rw-r--r--images/cursor/Wait.anibin0 -> 34500 bytes
-rw-r--r--images/cursor/arrow.curbin0 -> 4286 bytes
-rw-r--r--images/cursor/cross.curbin0 -> 4286 bytes
-rw-r--r--images/cursor/hand.curbin0 -> 7598 bytes
-rw-r--r--images/cursor/hand_writing.curbin0 -> 4286 bytes
-rw-r--r--images/cursor/up.curbin0 -> 4286 bytes
-rw-r--r--images/kcp-boxed-in.jpgbin0 -> 108417 bytes
-rw-r--r--images/music-circle-of-fifths.webpbin0 -> 14464 bytes
-rw-r--r--images/music-key-signatures.webpbin0 -> 15512 bytes
-rw-r--r--images/perspective-double-point.jpgbin0 -> 117066 bytes
-rw-r--r--images/perspective-horizon.jpgbin0 -> 50984 bytes
-rw-r--r--images/perspective-single-point.jpgbin0 -> 70157 bytes
-rw-r--r--images/stamp/brain-rot.gifbin0 -> 3651 bytes
-rw-r--r--images/stamp/fuck-u.gifbin0 -> 4429 bytes
-rw-r--r--notes/adhd.md11
-rw-r--r--notes/art.md17
-rw-r--r--notes/colour.md49
-rw-r--r--notes/computing.md12
-rw-r--r--notes/functional.md9
-rw-r--r--notes/haskell.md118
-rw-r--r--notes/index.md18
-rw-r--r--notes/kcp.md12
-rw-r--r--notes/lambda-calculus.md29
-rw-r--r--notes/music.md14
-rw-r--r--notes/operating-system.md23
-rw-r--r--notes/perspective.md30
-rw-r--r--notes/renoise.md107
-rw-r--r--notes/sable.md32
-rw-r--r--notes/scales.md23
-rw-r--r--notes/software.md42
-rw-r--r--notes/visual-art.md27
-rw-r--r--site.cabal36
-rw-r--r--src/Main.hs201
-rw-r--r--templates/card.html20
-rw-r--r--templates/default.html65
-rw-r--r--templates/recently-modified.md2
-rw-r--r--templates/recently-published.md2
61 files changed, 2160 insertions, 0 deletions
diff --git a/LICENSE b/LICENSE
new file mode 100644
index 0000000..c91ff99
--- /dev/null
+++ b/LICENSE
@@ -0,0 +1,13 @@
+Copyright (c) 2024 ~kcp <kitty@piapiac.org>
+
+Permission to use, copy, modify, and distribute this software for any
+purpose with or without fee is hereby granted, provided that the above
+copyright notice and this permission notice appear in all copies.
+
+THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
diff --git a/README.md b/README.md
new file mode 100644
index 0000000..9652baf
--- /dev/null
+++ b/README.md
@@ -0,0 +1,22 @@
+# static site generator
+this is my website i built using [hakyll][1]
+
+## copying
+feel free to use it too (see `LICENSE`) but this was built for myself, it's just open source in case some one wants to see how it works. you'd be better off making your own thingy with hakyll :p
+
+## dependencies
+GHC2021, Cabal, see `site.cabal`
+
+## building the executable
+`hakyll` can take a while to build because of its dependency on `pandoc` but your OS might provide dynamic haskell libraries for these in its packages
+
+if not, be prepared to have a fierce coffee or something while you wait
+
+* `cabal build` - build the executable
+* `cabal install` - install the executable to your $PATH
+
+## running the binary
+all files not in `src/` are the resources used to build the website
+
+* `site watch` - watch the website for updates and run a local server
+* `site build` - build the website
diff --git a/css/cursor.css b/css/cursor.css
new file mode 100644
index 0000000..7eabd15
--- /dev/null
+++ b/css/cursor.css
@@ -0,0 +1,3 @@
+* { cursor: url(/images/cursor/arrow.cur), auto; }
+a:hover { cursor: url(/images/cursor/up.cur), auto; }
+a img:hover { cursor: url(/images/cursor/up.cur), auto; }
diff --git a/css/default.css b/css/default.css
new file mode 100644
index 0000000..7c9882c
--- /dev/null
+++ b/css/default.css
@@ -0,0 +1,2 @@
+:root { --accent: deeppink; --accent-hover: skyblue; }
+@media (prefers-color-scheme: dark) { :root { --accent: pink; --accent-hover: deeppink } }
diff --git a/css/kate.css b/css/kate.css
new file mode 100644
index 0000000..5e7225c
--- /dev/null
+++ b/css/kate.css
@@ -0,0 +1,66 @@
+pre > code.sourceCode { white-space: pre; position: relative; }
+pre > code.sourceCode > span { display: inline-block; line-height: 1.25; }
+pre > code.sourceCode > span:empty { height: 1.2em; }
+.sourceCode { overflow: visible; }
+code.sourceCode > span { color: inherit; text-decoration: inherit; }
+div.sourceCode { margin: 1em 0; }
+pre.sourceCode { margin: 0; }
+@media screen {
+div.sourceCode { overflow: auto; }
+}
+@media print {
+pre > code.sourceCode { white-space: pre-wrap; }
+pre > code.sourceCode > span { text-indent: -5em; padding-left: 5em; }
+}
+pre.numberSource code
+ { counter-reset: source-line 0; }
+pre.numberSource code > span
+ { position: relative; left: -4em; counter-increment: source-line; }
+pre.numberSource code > span > a:first-child::before
+ { content: counter(source-line);
+ position: relative; left: -1em; text-align: right; vertical-align: baseline;
+ border: none; display: inline-block;
+ -webkit-touch-callout: none; -webkit-user-select: none;
+ -khtml-user-select: none; -moz-user-select: none;
+ -ms-user-select: none; user-select: none;
+ padding: 0 4px; width: 4em;
+ background-color: #ffffff;
+ color: #a0a0a0;
+ }
+pre.numberSource { margin-left: 3em; border-left: 1px solid #a0a0a0; padding-left: 4px; }
+div.sourceCode
+ { color: #1f1c1b; background-color: #ffffff; }
+@media screen {
+pre > code.sourceCode > span > a:first-child::before { text-decoration: underline; }
+}
+code span { color: #1f1c1b; } /* Normal */
+code span.al { color: #bf0303; background-color: #f7e6e6; font-weight: bold; } /* Alert */
+code span.an { color: #ca60ca; } /* Annotation */
+code span.at { color: #0057ae; } /* Attribute */
+code span.bn { color: #b08000; } /* BaseN */
+code span.bu { color: #644a9b; font-weight: bold; } /* BuiltIn */
+code span.cf { color: #1f1c1b; font-weight: bold; } /* ControlFlow */
+code span.ch { color: #924c9d; } /* Char */
+code span.cn { color: #aa5500; } /* Constant */
+code span.co { color: #898887; } /* Comment */
+code span.cv { color: #0095ff; } /* CommentVar */
+code span.do { color: #607880; } /* Documentation */
+code span.dt { color: #0057ae; } /* DataType */
+code span.dv { color: #b08000; } /* DecVal */
+code span.er { color: #bf0303; text-decoration: underline; } /* Error */
+code span.ex { color: #0095ff; font-weight: bold; } /* Extension */
+code span.fl { color: #b08000; } /* Float */
+code span.fu { color: #644a9b; } /* Function */
+code span.im { color: #ff5500; } /* Import */
+code span.in { color: #b08000; } /* Information */
+code span.kw { color: #1f1c1b; font-weight: bold; } /* Keyword */
+code span.op { color: #1f1c1b; } /* Operator */
+code span.ot { color: #006e28; } /* Other */
+code span.pp { color: #006e28; } /* Preprocessor */
+code span.re { color: #0057ae; background-color: #e0e9f8; } /* RegionMarker */
+code span.sc { color: #3daee9; } /* SpecialChar */
+code span.ss { color: #ff5500; } /* SpecialString */
+code span.st { color: #bf0303; } /* String */
+code span.va { color: #0057ae; } /* Variable */
+code span.vs { color: #bf0303; } /* VerbatimString */
+code span.wa { color: #bf0303; } /* Warning */
diff --git a/css/style.css b/css/style.css
new file mode 100644
index 0000000..acbba75
--- /dev/null
+++ b/css/style.css
@@ -0,0 +1,1155 @@
+/* PrefaceCSS <https://github.com/cluzier/PrefaceCSS> , Copyright 2019, Conner Luzier */
+/* Imports */
+@import url("https://fonts.googleapis.com/css?family=Roboto+Mono&display=swap");
+/* Define some variables we will need (colors) */
+/* Do the imports for other scss */
+* {
+ font-family: "Roboto Mono", monospace;
+ font-weight: 400;
+ color: #212121;
+ padding: 0;
+ margin: 0;
+ box-sizing: border-box;
+ word-wrap: break-word; }
+
+nav {
+ margin: 20px auto;
+ max-width: 960px;
+ width: 95%; }
+ nav label {
+ font-weight: bold; }
+ nav ul {
+ list-style: none;
+ float: right; }
+ nav ul li {
+ margin: 0 15px; }
+ nav * {
+ display: inline-block; }
+ @media screen and (max-width: 640px) {
+ nav {
+ text-align: center; }
+ nav > * {
+ display: block; }
+ nav ul {
+ float: none;
+ margin-top: 15px; } }
+
+.container {
+ max-width: 960px;
+ width: 95%;
+ margin: 0 auto; }
+
+.grid-flex {
+ display: flex; }
+
+.grid-flex-wrap {
+ display: flex;
+ flex-wrap: wrap; }
+
+.row:after {
+ content: '';
+ display: table;
+ clear: both; }
+
+.flex-1 {
+ flex: 1; }
+
+.flex-2 {
+ flex: 2; }
+
+.flex-3 {
+ flex: 3; }
+
+.flex-4 {
+ flex: 4; }
+
+.flex-5 {
+ flex: 5; }
+
+.flex-6 {
+ flex: 6; }
+
+.flex-7 {
+ flex: 7; }
+
+.flex-8 {
+ flex: 8; }
+
+.flex-9 {
+ flex: 9; }
+
+.flex-10 {
+ flex: 10; }
+
+/* Old grid system */
+.one.column, .one.columns {
+ width: 7.33333%;
+ display: inline-block;
+ margin: 0 .5%;
+ float: left; }
+
+.two.columns {
+ width: 15.66667%;
+ display: inline-block;
+ margin: 0 .5%;
+ float: left; }
+
+.three.columns {
+ width: 24%;
+ display: inline-block;
+ margin: 0 .5%;
+ float: left; }
+
+.four.columns {
+ width: 32.33333%;
+ display: inline-block;
+ margin: 0 .5%;
+ float: left; }
+
+.five.columns {
+ width: 40.66667%;
+ display: inline-block;
+ margin: 0 .5%;
+ float: left; }
+
+.six.columns {
+ width: 49%;
+ display: inline-block;
+ margin: 0 .5%;
+ float: left; }
+
+.seven.columns {
+ width: 57.33333%;
+ display: inline-block;
+ margin: 0 .5%;
+ float: left; }
+
+.eight.columns {
+ width: 65.66667%;
+ display: inline-block;
+ margin: 0 .5%;
+ float: left; }
+
+.nine.columns {
+ width: 74%;
+ display: inline-block;
+ margin: 0 .5%;
+ float: left; }
+
+.ten.columns {
+ width: 82.33333%;
+ display: inline-block;
+ margin: 0 .5%;
+ float: left; }
+
+.eleven.columns {
+ width: 90.66667%;
+ display: inline-block;
+ margin: 0 .5%;
+ float: left; }
+
+.twelve.columns {
+ width: 99%;
+ display: inline-block;
+ margin: 0 .5%;
+ float: left; }
+
+@media screen and (max-width: 750px) {
+ .columns.desktop, .column.desktop {
+ display: block !important;
+ width: 99% !important; } }
+
+@media screen and (min-width: 750px) {
+ .columns.mobile, .column.mobile {
+ display: block !important;
+ width: 99% !important; } }
+
+button {
+ display: inline-block;
+ padding: 8px 30px;
+ margin: 5px 0;
+ text-align: center;
+ text-transform: uppercase;
+ border: none;
+ outline: none;
+ border-radius: 2px;
+ color: #413a3a;
+ cursor: pointer;
+ background-color: #212121;
+ transition: 0.2s ease-in-out; }
+ button:hover, button:disabled {
+ background-color: #878787; }
+ button:active {
+ background-color: black; }
+ button.success {
+ background-color: #66cdaa;
+ transition: 0.2s ease-in-out; }
+ button.success:hover, button.success:disabled {
+ background-color: #a0e0ca; }
+ button.success:active {
+ background-color: #39ae86; }
+ button.error {
+ background-color: #f08080;
+ transition: 0.2s ease-in-out; }
+ button.error:hover, button.error:disabled {
+ background-color: #f8c4c4; }
+ button.error:active {
+ background-color: #e83c3c; }
+ button.warning {
+ background-color: #fef65b;
+ transition: 0.2s ease-in-out; }
+ button.warning:hover, button.warning:disabled {
+ background-color: #fefaa7; }
+ button.warning:active {
+ background-color: #fef20f; }
+ button.btn-shadow {
+ box-shadow: 0 3px 6px rgba(0, 0, 0, 0.16), 0 3px 6px rgba(0, 0, 0, 0.23); }
+ button.btn-shadow:hover {
+ box-shadow: 0 1px 3px rgba(0, 0, 0, 0.12), 0 1px 2px rgba(0, 0, 0, 0.24); }
+ button.btn-shadow:active {
+ box-shadow: 0 14px 28px rgba(0, 0, 0, 0.25), 0 10px 10px rgba(0, 0, 0, 0.22); }
+
+input, textarea, select {
+ display: block;
+ width: 100%;
+ padding: 5px;
+ margin: 5px 0;
+ border: 1px solid #c0c0c0;
+ outline: none;
+ border-radius: 2px;
+ background-color: #ffffff; }
+ input:hover, textarea:hover, select:hover {
+ border-color: #6e6e6e; }
+ input:focus, textarea:focus, select:focus {
+ border-color: #212121; }
+
+textarea {
+ resize: vertical;
+ min-height: 60px; }
+
+b, strong {
+ font-weight: 700; }
+
+a {
+ text-decoration: underline;
+ cursor: pointer; }
+ a:hover {
+ color: #6e6e6e; }
+ a.none {
+ text-decoration: none; }
+
+h1 {
+ font-size: 3rem; }
+
+h2 {
+ font-size: 2.6rem; }
+
+h3 {
+ font-size: 2.2rem; }
+
+h4 {
+ font-size: 1.8rem; }
+
+h5 {
+ font-size: 1.6rem; }
+
+h6 {
+ font-size: 1.4rem; }
+
+h1, h2, h3, h4, h5, h6 {
+ margin-bottom: 10px;
+ font-weight: bold; }
+
+small {
+ font-size: .8rem; }
+
+p {
+ margin: 15px 0; }
+
+.text-left {
+ text-align: left; }
+
+.text-center {
+ text-align: center; }
+
+.text-right {
+ text-align: right; }
+
+.text-muted, .text-muted * {
+ color: #616161; }
+
+.text-success, .text-success * {
+ color: #66cdaa; }
+
+.text-error, .text-error * {
+ color: #f08080; }
+
+.text-warning, .text-warning * {
+ color: #fef65b; }
+
+.text-inverted, .text-inverted * {
+ color: #413a3a; }
+
+table {
+ border-collapse: collapse;
+ width: 100%;
+ margin-top: .5em; }
+ @media screen and (max-width: 750px) {
+ table {
+ overflow-x: auto;
+ display: block; } }
+ table td, table th {
+ padding: 12px 15px;
+ text-align: left;
+ border-bottom: 1px solid #c0c0c0; }
+
+ul ul {
+ margin-left: 15px; }
+
+ul.inner, ul .inner {
+ list-style-position: inside; }
+
+ul.outer, ul .outer {
+ list-style-position: outside; }
+
+/**
+ * Thanks to Chris Bracco for this tooltip from https://codepen.io/cbracco/pen/qzukg
+ */
+/* Add this attribute to the element that needs a tooltip */
+[data-tooltip] {
+ position: relative;
+ z-index: 2;
+ cursor: pointer; }
+
+/* Hide the tooltip content by default */
+[data-tooltip]:before,
+[data-tooltip]:after {
+ visibility: hidden;
+ -ms-filter: "progid:DXImageTransform.Microsoft.Alpha(Opacity=0)";
+ opacity: 0;
+ pointer-events: none; }
+
+/* Position tooltip above the element */
+[data-tooltip]:before {
+ position: absolute;
+ bottom: 150%;
+ left: 50%;
+ margin-bottom: 5px;
+ margin-left: -80px;
+ padding: 7px;
+ width: 160px;
+ border-radius: 3px;
+ background-color: #000;
+ background-color: rgba(51, 51, 51, 0.9);
+ color: #fff;
+ content: attr(data-tooltip);
+ text-align: center;
+ font-size: 14px;
+ line-height: 1.2; }
+
+/* Triangle hack to make tooltip look like a speech bubble */
+[data-tooltip]:after {
+ position: absolute;
+ bottom: 150%;
+ left: 50%;
+ margin-left: -5px;
+ width: 0;
+ border-top: 5px solid #000;
+ border-top: 5px solid rgba(51, 51, 51, 0.9);
+ border-right: 5px solid transparent;
+ border-left: 5px solid transparent;
+ content: " ";
+ font-size: 0;
+ line-height: 0; }
+
+/* Show tooltip content on hover */
+[data-tooltip]:hover:before,
+[data-tooltip]:hover:after {
+ visibility: visible;
+ -ms-filter: "progid:DXImageTransform.Microsoft.Alpha(Opacity=100)";
+ opacity: 1; }
+
+.card {
+ margin: 15px;
+ border-radius: 1px;
+ border: 0.5px solid #c0c0c0; }
+ .card img {
+ width: 100%;
+ display: block;
+ border-radius: 1px 1px 0 0; }
+ .card .content {
+ padding: 10px; }
+ .card .footer {
+ border-top: 1px solid #c0c0c0;
+ padding: 5px 15px;
+ text-align: right; }
+ .card.card-shadow {
+ box-shadow: 0 3px 6px rgba(0, 0, 0, 0.16), 0 3px 6px rgba(0, 0, 0, 0.23);
+ border: none; }
+
+hr {
+ border: none;
+ outline: none;
+ height: 1px;
+ width: 60%;
+ background-color: #c0c0c0; }
+
+.bg-success {
+ background-color: #66cdaa; }
+
+.bg-warning {
+ background-color: #fef65b; }
+
+.bg-error {
+ background-color: #f08080; }
+
+.bg-success-bright {
+ background-color: #66cdaa; }
+
+.bg-warning-bright {
+ background-color: #fef65b; }
+
+.bg-error-bright {
+ background-color: #f08080; }
+
+code {
+ background-color: #c0c0c0;
+ display: inline-block;
+ padding: 4px 8px;
+ font-family: monospace;
+ overflow-x: auto; }
+
+pre > code {
+ display: block;
+ white-space: pre; }
+
+.cover {
+ min-height: 60vh;
+ display: flex;
+ align-items: center;
+ justify-content: center; }
+ .cover > div {
+ flex: 1; }
+
+.progress {
+ display: block;
+ width: 100%;
+ height: 3px;
+ margin: 5px;
+ background-color: #c0c0c0; }
+ .progress .bar {
+ transition: width .5s ease-in-out;
+ display: block;
+ height: 100%;
+ width: 100%;
+ max-width: 100%; }
+
+img {
+ display: block;
+ width: 100%; }
+
+.pagination {
+ display: flex;
+ width: 100%;
+ text-align: center;
+ padding: 15px; }
+ .pagination * {
+ text-decoration: none; }
+ .pagination > * {
+ flex: 1;
+ padding: 3px; }
+ .pagination > *:hover {
+ background-color: #cdcdcd;
+ transition: .3s background-color ease-in-out; }
+ .pagination > *:hover, .pagination > *:hover * {
+ color: #212121; }
+ .pagination .action {
+ flex: .5; }
+ .pagination.no-flex {
+ display: block; }
+ .pagination.no-flex > * {
+ display: inline-block;
+ margin: 0 10px; }
+
+.lds-facebook {
+ display: inline-block;
+ position: relative;
+ margin: 25px;
+ width: 64px;
+ height: 64px; }
+
+.lds-facebook div {
+ display: inline-block;
+ position: absolute;
+ left: 6px;
+ width: 13px;
+ background: #413a3a;
+ animation: lds-facebook 1.2s cubic-bezier(0, 0.5, 0.5, 1) infinite; }
+
+.lds-facebook div:nth-child(1) {
+ left: 6px;
+ animation-delay: -0.24s; }
+
+.lds-facebook div:nth-child(2) {
+ left: 26px;
+ animation-delay: -0.12s; }
+
+.lds-facebook div:nth-child(3) {
+ left: 45px;
+ animation-delay: 0; }
+
+@keyframes lds-facebook {
+ 0% {
+ top: 6px;
+ height: 51px; }
+ 50%, 100% {
+ top: 19px;
+ height: 26px; } }
+
+.lds-roller {
+ display: inline-block;
+ position: relative;
+ margin: 25px;
+ width: 64px;
+ height: 64px; }
+
+.lds-roller div {
+ animation: lds-roller 1.2s cubic-bezier(0.5, 0, 0.5, 1) infinite;
+ transform-origin: 32px 32px; }
+
+.lds-roller div:after {
+ content: " ";
+ display: block;
+ position: absolute;
+ width: 6px;
+ height: 6px;
+ border-radius: 50%;
+ background: #413a3a;
+ margin: -3px 0 0 -3px; }
+
+.lds-roller div:nth-child(1) {
+ animation-delay: -0.036s; }
+
+.lds-roller div:nth-child(1):after {
+ top: 50px;
+ left: 50px; }
+
+.lds-roller div:nth-child(2) {
+ animation-delay: -0.072s; }
+
+.lds-roller div:nth-child(2):after {
+ top: 54px;
+ left: 45px; }
+
+.lds-roller div:nth-child(3) {
+ animation-delay: -0.108s; }
+
+.lds-roller div:nth-child(3):after {
+ top: 57px;
+ left: 39px; }
+
+.lds-roller div:nth-child(4) {
+ animation-delay: -0.144s; }
+
+.lds-roller div:nth-child(4):after {
+ top: 58px;
+ left: 32px; }
+
+.lds-roller div:nth-child(5) {
+ animation-delay: -0.18s; }
+
+.lds-roller div:nth-child(5):after {
+ top: 57px;
+ left: 25px; }
+
+.lds-roller div:nth-child(6) {
+ animation-delay: -0.216s; }
+
+.lds-roller div:nth-child(6):after {
+ top: 54px;
+ left: 19px; }
+
+.lds-roller div:nth-child(7) {
+ animation-delay: -0.252s; }
+
+.lds-roller div:nth-child(7):after {
+ top: 50px;
+ left: 14px; }
+
+.lds-roller div:nth-child(8) {
+ animation-delay: -0.288s; }
+
+.lds-roller div:nth-child(8):after {
+ top: 45px;
+ left: 10px; }
+
+@keyframes lds-roller {
+ 0% {
+ transform: rotate(0deg); }
+ 100% {
+ transform: rotate(360deg); } }
+
+.lds-ripple {
+ display: inline-block;
+ position: relative;
+ margin: 25px;
+ width: 64px;
+ height: 64px; }
+
+.lds-ripple div {
+ position: absolute;
+ border: 4px solid #413a3a;
+ opacity: 1;
+ border-radius: 50%;
+ animation: lds-ripple 1s cubic-bezier(0, 0.2, 0.8, 1) infinite; }
+
+.lds-ripple div:nth-child(2) {
+ animation-delay: -0.5s; }
+
+@keyframes lds-ripple {
+ 0% {
+ top: 28px;
+ left: 28px;
+ width: 0;
+ height: 0;
+ opacity: 1; }
+ 100% {
+ top: -1px;
+ left: -1px;
+ width: 58px;
+ height: 58px;
+ opacity: 0; } }
+
+.lds-ellipsis {
+ display: inline-block;
+ position: relative;
+ margin: 25px;
+ width: 64px;
+ height: 64px; }
+
+.lds-ellipsis div {
+ position: absolute;
+ top: 27px;
+ width: 11px;
+ height: 11px;
+ border-radius: 50%;
+ background: #413a3a;
+ animation-timing-function: cubic-bezier(0, 1, 1, 0); }
+
+.lds-ellipsis div:nth-child(1) {
+ left: 6px;
+ animation: lds-ellipsis1 0.6s infinite; }
+
+.lds-ellipsis div:nth-child(2) {
+ left: 6px;
+ animation: lds-ellipsis2 0.6s infinite; }
+
+.lds-ellipsis div:nth-child(3) {
+ left: 26px;
+ animation: lds-ellipsis2 0.6s infinite; }
+
+.lds-ellipsis div:nth-child(4) {
+ left: 45px;
+ animation: lds-ellipsis3 0.6s infinite; }
+
+@keyframes lds-ellipsis1 {
+ 0% {
+ transform: scale(0); }
+ 100% {
+ transform: scale(1); } }
+
+@keyframes lds-ellipsis3 {
+ 0% {
+ transform: scale(1); }
+ 100% {
+ transform: scale(0); } }
+
+@keyframes lds-ellipsis2 {
+ 0% {
+ transform: translate(0, 0); }
+ 100% {
+ transform: translate(19px, 0); } }
+
+.lds-circle {
+ display: inline-block;
+ margin: 25px;
+ transform: translateZ(1px); }
+
+.lds-circle > div {
+ display: inline-block;
+ width: 51px;
+ height: 51px;
+ margin: 6px;
+ border-radius: 50%;
+ background: #413a3a;
+ animation: lds-circle 2.4s cubic-bezier(0, 0.2, 0.8, 1) infinite; }
+
+@keyframes lds-circle {
+ 0%, 100% {
+ animation-timing-function: cubic-bezier(0.5, 0, 1, 0.5); }
+ 0% {
+ transform: rotateY(0deg); }
+ 50% {
+ transform: rotateY(1800deg);
+ animation-timing-function: cubic-bezier(0, 0.5, 0.5, 1); }
+ 100% {
+ transform: rotateY(3600deg); } }
+
+.lds-hourglass {
+ display: inline-block;
+ position: relative;
+ margin: 25px;
+ width: 64px;
+ height: 64px; }
+
+.lds-hourglass:after {
+ content: " ";
+ display: block;
+ border-radius: 50%;
+ width: 0;
+ height: 0;
+ margin: 6px;
+ box-sizing: border-box;
+ border: 26px solid #413a3a;
+ border-color: #413a3a transparent #413a3a transparent;
+ animation: lds-hourglass 1.2s infinite; }
+
+@keyframes lds-hourglass {
+ 0% {
+ transform: rotate(0);
+ animation-timing-function: cubic-bezier(0.55, 0.055, 0.675, 0.19); }
+ 50% {
+ transform: rotate(900deg);
+ animation-timing-function: cubic-bezier(0.215, 0.61, 0.355, 1); }
+ 100% {
+ transform: rotate(1800deg); } }
+
+.spinner {
+ position: relative;
+ display: inline-block;
+ margin: 25px;
+ width: 64px;
+ height: 64px; }
+
+.cube1, .cube2 {
+ background-color: #413a3a;
+ width: 15px;
+ height: 15px;
+ position: absolute;
+ top: 0;
+ left: 0;
+ animation: sk-cubemove 1.8s infinite ease-in-out; }
+
+.cube2 {
+ animation-delay: -0.9s; }
+
+@keyframes sk-cubemove {
+ 25% {
+ transform: translateX(42px) rotate(-90deg) scale(0.5);
+ -webkit-transform: translateX(42px) rotate(-90deg) scale(0.5); }
+ 50% {
+ transform: translateX(42px) translateY(42px) rotate(-179deg);
+ -webkit-transform: translateX(42px) translateY(42px) rotate(-179deg); }
+ 50.1% {
+ transform: translateX(42px) translateY(42px) rotate(-180deg);
+ -webkit-transform: translateX(42px) translateY(42px) rotate(-180deg); }
+ 75% {
+ transform: translateX(0px) translateY(42px) rotate(-270deg) scale(0.5);
+ -webkit-transform: translateX(0px) translateY(42px) rotate(-270deg) scale(0.5); }
+ 100% {
+ transform: rotate(-360deg);
+ -webkit-transform: rotate(-360deg); } }
+
+.box-spinner {
+ position: relative;
+ display: inline-block;
+ margin: 25px;
+ width: 64px;
+ height: 64px;
+ background-color: #413a3a;
+ animation: sk-rotateplane 1.2s infinite ease-in-out; }
+
+@keyframes sk-rotateplane {
+ 0% {
+ transform: perspective(120px) rotateX(0deg) rotateY(0deg);
+ -webkit-transform: perspective(120px) rotateX(0deg) rotateY(0deg); }
+ 50% {
+ transform: perspective(120px) rotateX(-180.1deg) rotateY(0deg);
+ -webkit-transform: perspective(120px) rotateX(-180.1deg) rotateY(0deg); }
+ 100% {
+ transform: perspective(120px) rotateX(-180deg) rotateY(-179.9deg);
+ -webkit-transform: perspective(120px) rotateX(-180deg) rotateY(-179.9deg); } }
+
+.sk-cube-grid {
+ position: relative;
+ display: inline-block;
+ margin: 25px;
+ width: 64px;
+ height: 64px; }
+
+.sk-cube-grid .sk-cube {
+ width: 33%;
+ height: 33%;
+ background-color: #413a3a;
+ float: left;
+ animation: sk-cubeGridScaleDelay 1.3s infinite ease-in-out; }
+
+.sk-cube-grid .sk-cube1 {
+ animation-delay: 0.2s; }
+
+.sk-cube-grid .sk-cube2 {
+ animation-delay: 0.3s; }
+
+.sk-cube-grid .sk-cube3 {
+ animation-delay: 0.4s; }
+
+.sk-cube-grid .sk-cube4 {
+ animation-delay: 0.1s; }
+
+.sk-cube-grid .sk-cube5 {
+ animation-delay: 0.2s; }
+
+.sk-cube-grid .sk-cube6 {
+ animation-delay: 0.3s; }
+
+.sk-cube-grid .sk-cube7 {
+ animation-delay: 0s; }
+
+.sk-cube-grid .sk-cube8 {
+ animation-delay: 0.1s; }
+
+.sk-cube-grid .sk-cube9 {
+ animation-delay: 0.2s; }
+
+@keyframes sk-cubeGridScaleDelay {
+ 0%, 70%, 100% {
+ transform: scale3D(1, 1, 1); }
+ 35% {
+ transform: scale3D(0, 0, 1); } }
+
+.sk-folding-cube {
+ margin: 25px;
+ width: 40px;
+ height: 40px;
+ position: relative;
+ display: inline-block;
+ transform: rotateZ(45deg); }
+
+.sk-folding-cube .sk-cube {
+ float: left;
+ width: 50%;
+ height: 50%;
+ position: relative;
+ transform: scale(1.1); }
+
+.sk-folding-cube .sk-cube:before {
+ content: '';
+ position: absolute;
+ top: 0;
+ left: 0;
+ width: 100%;
+ height: 100%;
+ background-color: #413a3a;
+ animation: sk-foldCubeAngle 2.4s infinite linear both;
+ transform-origin: 100% 100%; }
+
+.sk-folding-cube .sk-cube2 {
+ transform: scale(1.1) rotateZ(90deg); }
+
+.sk-folding-cube .sk-cube3 {
+ transform: scale(1.1) rotateZ(180deg); }
+
+.sk-folding-cube .sk-cube4 {
+ transform: scale(1.1) rotateZ(270deg); }
+
+.sk-folding-cube .sk-cube2:before {
+ animation-delay: 0.3s; }
+
+.sk-folding-cube .sk-cube3:before {
+ animation-delay: 0.6s; }
+
+.sk-folding-cube .sk-cube4:before {
+ animation-delay: 0.9s; }
+
+@keyframes sk-foldCubeAngle {
+ 0%, 10% {
+ transform: perspective(140px) rotateX(-180deg);
+ opacity: 0; }
+ 25%, 75% {
+ transform: perspective(140px) rotateX(0deg);
+ opacity: 1; }
+ 90%, 100% {
+ transform: perspective(140px) rotateY(180deg);
+ opacity: 0; } }
+
+.scrollbar {
+ margin-left: 30px;
+ float: left;
+ height: 300px;
+ width: 65px;
+ background: #F5F5F5;
+ overflow-y: scroll;
+ margin-bottom: 25px; }
+
+.force-overflow {
+ min-height: 450px; }
+
+#wrapper {
+ text-align: center;
+ width: 500px;
+ margin: auto; }
+
+/*
+ * STYLE 1
+ */
+#style-1::-webkit-scrollbar-track {
+ -webkit-box-shadow: inset 0 0 6px rgba(0, 0, 0, 0.3);
+ border-radius: 10px;
+ background-color: #F5F5F5; }
+
+#style-1::-webkit-scrollbar {
+ width: 12px;
+ background-color: #F5F5F5; }
+
+#style-1::-webkit-scrollbar-thumb {
+ border-radius: 10px;
+ -webkit-box-shadow: inset 0 0 6px rgba(0, 0, 0, 0.3);
+ background-color: #555; }
+
+/*
+ * STYLE 2
+ */
+#style-2::-webkit-scrollbar-track {
+ -webkit-box-shadow: inset 0 0 6px rgba(0, 0, 0, 0.3);
+ border-radius: 10px;
+ background-color: #F5F5F5; }
+
+#style-2::-webkit-scrollbar {
+ width: 12px;
+ background-color: #F5F5F5; }
+
+#style-2::-webkit-scrollbar-thumb {
+ border-radius: 10px;
+ -webkit-box-shadow: inset 0 0 6px rgba(0, 0, 0, 0.3);
+ background-color: #D62929; }
+
+/*
+ * STYLE 3
+ */
+#style-3::-webkit-scrollbar-track {
+ -webkit-box-shadow: inset 0 0 6px rgba(0, 0, 0, 0.3);
+ background-color: #F5F5F5; }
+
+#style-3::-webkit-scrollbar {
+ width: 6px;
+ background-color: #F5F5F5; }
+
+#style-3::-webkit-scrollbar-thumb {
+ background-color: #000000; }
+
+/*
+ * STYLE 4
+ */
+#style-4::-webkit-scrollbar-track {
+ -webkit-box-shadow: inset 0 0 6px rgba(0, 0, 0, 0.3);
+ background-color: #F5F5F5; }
+
+#style-4::-webkit-scrollbar {
+ width: 10px;
+ background-color: #F5F5F5; }
+
+#style-4::-webkit-scrollbar-thumb {
+ background-color: #000000;
+ border: 2px solid #555555; }
+
+/*
+ * STYLE 5
+ */
+#style-5::-webkit-scrollbar-track {
+ -webkit-box-shadow: inset 0 0 6px rgba(0, 0, 0, 0.3);
+ background-color: #F5F5F5; }
+
+#style-5::-webkit-scrollbar {
+ width: 10px;
+ background-color: #F5F5F5; }
+
+#style-5::-webkit-scrollbar-thumb {
+ background-color: #0ae;
+ background-image: -webkit-gradient(linear, 0 0, 0 100%, color-stop(0.5, rgba(255, 255, 255, 0.2)), color-stop(0.5, transparent), to(transparent)); }
+
+/*
+ * STYLE 6
+ */
+#style-6::-webkit-scrollbar-track {
+ -webkit-box-shadow: inset 0 0 6px rgba(0, 0, 0, 0.3);
+ background-color: #F5F5F5; }
+
+#style-6::-webkit-scrollbar {
+ width: 10px;
+ background-color: #F5F5F5; }
+
+#style-6::-webkit-scrollbar-thumb {
+ background-color: #F90;
+ background-image: -webkit-linear-gradient(45deg, rgba(255, 255, 255, 0.2) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.2) 50%, rgba(255, 255, 255, 0.2) 75%, transparent 75%, transparent); }
+
+/*
+ * STYLE 7
+ */
+#style-7::-webkit-scrollbar-track {
+ -webkit-box-shadow: inset 0 0 6px rgba(0, 0, 0, 0.3);
+ background-color: #F5F5F5;
+ border-radius: 10px; }
+
+#style-7::-webkit-scrollbar {
+ width: 10px;
+ background-color: #F5F5F5; }
+
+#style-7::-webkit-scrollbar-thumb {
+ border-radius: 10px;
+ background-image: -webkit-gradient(linear, left bottom, left top, color-stop(0.44, #7a99d9), color-stop(0.72, #497dbd), color-stop(0.86, #1c3a94)); }
+
+/*
+ * STYLE 8
+ */
+#style-8::-webkit-scrollbar-track {
+ border: 1px solid black;
+ background-color: #F5F5F5; }
+
+#style-8::-webkit-scrollbar {
+ width: 10px;
+ background-color: #F5F5F5; }
+
+#style-8::-webkit-scrollbar-thumb {
+ background-color: #000000; }
+
+/*
+ * STYLE 9
+ */
+#style-9::-webkit-scrollbar-track {
+ -webkit-box-shadow: inset 0 0 6px rgba(0, 0, 0, 0.3);
+ background-color: #F5F5F5; }
+
+#style-9::-webkit-scrollbar {
+ width: 10px;
+ background-color: #F5F5F5; }
+
+#style-9::-webkit-scrollbar-thumb {
+ background-color: #F90;
+ background-image: -webkit-linear-gradient(90deg, rgba(255, 255, 255, 0.2) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.2) 50%, rgba(255, 255, 255, 0.2) 75%, transparent 75%, transparent); }
+
+/*
+ * STYLE 10
+ */
+#style-10::-webkit-scrollbar-track {
+ -webkit-box-shadow: inset 0 0 6px rgba(0, 0, 0, 0.3);
+ background-color: #F5F5F5;
+ border-radius: 10px; }
+
+#style-10::-webkit-scrollbar {
+ width: 10px;
+ background-color: #F5F5F5; }
+
+#style-10::-webkit-scrollbar-thumb {
+ background-color: #AAA;
+ border-radius: 10px;
+ background-image: -webkit-linear-gradient(90deg, rgba(0, 0, 0, 0.2) 25%, transparent 25%, transparent 50%, rgba(0, 0, 0, 0.2) 50%, rgba(0, 0, 0, 0.2) 75%, transparent 75%, transparent); }
+
+/*
+ * STYLE 11
+ */
+#style-11::-webkit-scrollbar-track {
+ -webkit-box-shadow: inset 0 0 6px rgba(0, 0, 0, 0.3);
+ background-color: #F5F5F5;
+ border-radius: 10px; }
+
+#style-11::-webkit-scrollbar {
+ width: 10px;
+ background-color: #F5F5F5; }
+
+#style-11::-webkit-scrollbar-thumb {
+ background-color: #3366FF;
+ border-radius: 10px;
+ background-image: -webkit-linear-gradient(0deg, rgba(255, 255, 255, 0.5) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.5) 50%, rgba(255, 255, 255, 0.5) 75%, transparent 75%, transparent); }
+
+/*
+ * STYLE 12
+ */
+#style-12::-webkit-scrollbar-track {
+ -webkit-box-shadow: inset 0 0 6px rgba(0, 0, 0, 0.9);
+ border-radius: 10px;
+ background-color: #444444; }
+
+#style-12::-webkit-scrollbar {
+ width: 12px;
+ background-color: #F5F5F5; }
+
+#style-12::-webkit-scrollbar-thumb {
+ border-radius: 10px;
+ background-color: #D62929;
+ background-image: -webkit-linear-gradient(90deg, transparent, rgba(0, 0, 0, 0.4) 50%, transparent, transparent); }
+
+/*
+ * STYLE 13
+ */
+#style-13::-webkit-scrollbar-track {
+ -webkit-box-shadow: inset 0 0 6px rgba(0, 0, 0, 0.9);
+ border-radius: 10px;
+ background-color: #CCCCCC; }
+
+#style-13::-webkit-scrollbar {
+ width: 12px;
+ background-color: #F5F5F5; }
+
+#style-13::-webkit-scrollbar-thumb {
+ border-radius: 10px;
+ background-color: #D62929;
+ background-image: -webkit-linear-gradient(90deg, transparent, rgba(0, 0, 0, 0.4) 50%, transparent, transparent); }
+
+/*
+ * STYLE 14
+ */
+#style-14::-webkit-scrollbar-track {
+ -webkit-box-shadow: inset 0 0 6px rgba(0, 0, 0, 0.6);
+ background-color: #CCCCCC; }
+
+#style-14::-webkit-scrollbar {
+ width: 10px;
+ background-color: #F5F5F5; }
+
+#style-14::-webkit-scrollbar-thumb {
+ background-color: #FFF;
+ background-image: -webkit-linear-gradient(90deg, black 0%, black 25%, transparent 100%, black 75%, transparent); }
+
+/*
+ * STYLE 15
+ */
+#style-15::-webkit-scrollbar-track {
+ -webkit-box-shadow: inset 0 0 6px rgba(0, 0, 0, 0.1);
+ background-color: #F5F5F5;
+ border-radius: 10px; }
+
+#style-15::-webkit-scrollbar {
+ width: 10px;
+ background-color: #F5F5F5; }
+
+#style-15::-webkit-scrollbar-thumb {
+ border-radius: 10px;
+ background-color: #FFF;
+ background-image: -webkit-gradient(linear, 40% 0%, 75% 84%, from(#4D9C41), to(#19911D), color-stop(0.6, #54DE5D)); }
+
+/*
+ * STYLE 16
+ */
+#style-16::-webkit-scrollbar-track {
+ -webkit-box-shadow: inset 0 0 6px rgba(0, 0, 0, 0.1);
+ background-color: #F5F5F5;
+ border-radius: 10px; }
+
+#style-16::-webkit-scrollbar {
+ width: 10px;
+ background-color: #F5F5F5; }
+
+#style-16::-webkit-scrollbar-thumb {
+ border-radius: 10px;
+ background-color: #FFF;
+ background-image: -webkit-linear-gradient(top, #e4f5fc 0%, #bfe8f9 50%, #9fd8ef 51%, #2ab0ed 100%); }
diff --git a/images/art-tampere.jpg b/images/art-tampere.jpg
new file mode 100644
index 0000000..3ae0a52
--- /dev/null
+++ b/images/art-tampere.jpg
Binary files differ
diff --git a/images/button/email.gif b/images/button/email.gif
new file mode 100644
index 0000000..23425f3
--- /dev/null
+++ b/images/button/email.gif
Binary files differ
diff --git a/images/button/haskell.gif b/images/button/haskell.gif
new file mode 100644
index 0000000..4546932
--- /dev/null
+++ b/images/button/haskell.gif
Binary files differ
diff --git a/images/button/paws.gif b/images/button/paws.gif
new file mode 100644
index 0000000..68cc833
--- /dev/null
+++ b/images/button/paws.gif
Binary files differ
diff --git a/images/button/under-construction.gif b/images/button/under-construction.gif
new file mode 100644
index 0000000..b95bda5
--- /dev/null
+++ b/images/button/under-construction.gif
Binary files differ
diff --git a/images/colour-beach-hue.jpg b/images/colour-beach-hue.jpg
new file mode 100644
index 0000000..1ff4e42
--- /dev/null
+++ b/images/colour-beach-hue.jpg
Binary files differ
diff --git a/images/colour-beach-monochrome.jpg b/images/colour-beach-monochrome.jpg
new file mode 100644
index 0000000..142c891
--- /dev/null
+++ b/images/colour-beach-monochrome.jpg
Binary files differ
diff --git a/images/colour-krita.webp b/images/colour-krita.webp
new file mode 100644
index 0000000..92b4d14
--- /dev/null
+++ b/images/colour-krita.webp
Binary files differ
diff --git a/images/colour-saturation.png b/images/colour-saturation.png
new file mode 100644
index 0000000..28aa333
--- /dev/null
+++ b/images/colour-saturation.png
Binary files differ
diff --git a/images/cursor/AppStarting.ani b/images/cursor/AppStarting.ani
new file mode 100644
index 0000000..d5ee81a
--- /dev/null
+++ b/images/cursor/AppStarting.ani
Binary files differ
diff --git a/images/cursor/Help.cur b/images/cursor/Help.cur
new file mode 100644
index 0000000..6405a20
--- /dev/null
+++ b/images/cursor/Help.cur
Binary files differ
diff --git a/images/cursor/IBeam.cur b/images/cursor/IBeam.cur
new file mode 100644
index 0000000..2cad186
--- /dev/null
+++ b/images/cursor/IBeam.cur
Binary files differ
diff --git a/images/cursor/No.cur b/images/cursor/No.cur
new file mode 100644
index 0000000..4130b00
--- /dev/null
+++ b/images/cursor/No.cur
Binary files differ
diff --git a/images/cursor/SizeAll.cur b/images/cursor/SizeAll.cur
new file mode 100644
index 0000000..471e95c
--- /dev/null
+++ b/images/cursor/SizeAll.cur
Binary files differ
diff --git a/images/cursor/SizeNESW.cur b/images/cursor/SizeNESW.cur
new file mode 100644
index 0000000..81698fb
--- /dev/null
+++ b/images/cursor/SizeNESW.cur
Binary files differ
diff --git a/images/cursor/SizeNS.cur b/images/cursor/SizeNS.cur
new file mode 100644
index 0000000..232ef25
--- /dev/null
+++ b/images/cursor/SizeNS.cur
Binary files differ
diff --git a/images/cursor/SizeNWSE.cur b/images/cursor/SizeNWSE.cur
new file mode 100644
index 0000000..e73c884
--- /dev/null
+++ b/images/cursor/SizeNWSE.cur
Binary files differ
diff --git a/images/cursor/SizeWE.cur b/images/cursor/SizeWE.cur
new file mode 100644
index 0000000..2983b67
--- /dev/null
+++ b/images/cursor/SizeWE.cur
Binary files differ
diff --git a/images/cursor/Wait.ani b/images/cursor/Wait.ani
new file mode 100644
index 0000000..e9b868e
--- /dev/null
+++ b/images/cursor/Wait.ani
Binary files differ
diff --git a/images/cursor/arrow.cur b/images/cursor/arrow.cur
new file mode 100644
index 0000000..7bdeee3
--- /dev/null
+++ b/images/cursor/arrow.cur
Binary files differ
diff --git a/images/cursor/cross.cur b/images/cursor/cross.cur
new file mode 100644
index 0000000..cee59e5
--- /dev/null
+++ b/images/cursor/cross.cur
Binary files differ
diff --git a/images/cursor/hand.cur b/images/cursor/hand.cur
new file mode 100644
index 0000000..d877884
--- /dev/null
+++ b/images/cursor/hand.cur
Binary files differ
diff --git a/images/cursor/hand_writing.cur b/images/cursor/hand_writing.cur
new file mode 100644
index 0000000..d45bebe
--- /dev/null
+++ b/images/cursor/hand_writing.cur
Binary files differ
diff --git a/images/cursor/up.cur b/images/cursor/up.cur
new file mode 100644
index 0000000..d9aea97
--- /dev/null
+++ b/images/cursor/up.cur
Binary files differ
diff --git a/images/kcp-boxed-in.jpg b/images/kcp-boxed-in.jpg
new file mode 100644
index 0000000..551e173
--- /dev/null
+++ b/images/kcp-boxed-in.jpg
Binary files differ
diff --git a/images/music-circle-of-fifths.webp b/images/music-circle-of-fifths.webp
new file mode 100644
index 0000000..d251daa
--- /dev/null
+++ b/images/music-circle-of-fifths.webp
Binary files differ
diff --git a/images/music-key-signatures.webp b/images/music-key-signatures.webp
new file mode 100644
index 0000000..4748ad6
--- /dev/null
+++ b/images/music-key-signatures.webp
Binary files differ
diff --git a/images/perspective-double-point.jpg b/images/perspective-double-point.jpg
new file mode 100644
index 0000000..bdd26f8
--- /dev/null
+++ b/images/perspective-double-point.jpg
Binary files differ
diff --git a/images/perspective-horizon.jpg b/images/perspective-horizon.jpg
new file mode 100644
index 0000000..bc285a2
--- /dev/null
+++ b/images/perspective-horizon.jpg
Binary files differ
diff --git a/images/perspective-single-point.jpg b/images/perspective-single-point.jpg
new file mode 100644
index 0000000..1709041
--- /dev/null
+++ b/images/perspective-single-point.jpg
Binary files differ
diff --git a/images/stamp/brain-rot.gif b/images/stamp/brain-rot.gif
new file mode 100644
index 0000000..0f04c86
--- /dev/null
+++ b/images/stamp/brain-rot.gif
Binary files differ
diff --git a/images/stamp/fuck-u.gif b/images/stamp/fuck-u.gif
new file mode 100644
index 0000000..2370eb4
--- /dev/null
+++ b/images/stamp/fuck-u.gif
Binary files differ
diff --git a/notes/adhd.md b/notes/adhd.md
new file mode 100644
index 0000000..8c12e16
--- /dev/null
+++ b/notes/adhd.md
@@ -0,0 +1,11 @@
+---
+title: adhd
+published: 2024-06-23T08:11:57+0100
+---
+
+## a neurodevelopmental disorder that causes executive dysfunction
+
+::::: {.links}
+- [additude magazine sleep guide](https://www.additudemag.com/download/how-to-sleep-better-adhd/)
+- [diagnosis pathways for adult adhd (uk)](https://adhduk.co.uk/diagnosis-pathways/)
+:::::
diff --git a/notes/art.md b/notes/art.md
new file mode 100644
index 0000000..0ca9142
--- /dev/null
+++ b/notes/art.md
@@ -0,0 +1,17 @@
+---
+title: art
+published: 2024-06-23T07:28:48+0100
+children:
+ - music
+ - visual-art
+---
+
+<p class="notice">Art variously involves creating beauty, expressing emotion, and representating conceptual ideas. This is a **section** that contains my personal notes on creating different forms of it.</p>
+
+### Ephemeral
+![This wall will be painted over or demolished](/images/art-tampere.jpg)
+
+## Out
+:::::{.links}
+- ["The Death of the Author" by Roland Barthes](https://www.ubu.com/aspen/aspen5and6/threeEssays.html#barthes)
+:::::
diff --git a/notes/colour.md b/notes/colour.md
new file mode 100644
index 0000000..818b26d
--- /dev/null
+++ b/notes/colour.md
@@ -0,0 +1,49 @@
+---
+title: colour
+published: 2024-06-23T07:28:48+0100
+---
+
+<style>.md45{width:47.5%}</style>
+
+<p class="notice">
+Colour is a very powerful visual element. Understanding it can enable more effective composition in your art.
+</p>
+
+<details>
+<summary>Hues are biased toward certain values</summary>
+<p>
+<img class="md45" src="/images/colour-beach-hue.jpg">
+<img class="md45" src="/images/colour-beach-monochrome.jpg">
+</p>
+<p>Due to the way human eyes perceive light, [value] is dependent on [hue].</p>
+</details>
+
+<details>
+<summary>Krita tips</summary>
+<p>You can preview a perceptually monochrome version of your illustration under the **softproofing** tab in the image properties.</p>
+<p><img src="/images/colour-krita.webp"></p>
+</details>
+
+<section>
+- [Properties]
+ - [Value]
+ - [Hue]
+ - [Saturation]
+</section>
+
+<section>
+## Properties
+
+### Value
+Simply how light or dark a given colour is. Value is mostly what creates contrast in a drawing.
+
+### Hue
+The pure pigment, without any tint or shading.
+
+### Saturation
+The intensity of the colour, i.e. it's difference from greyscale.
+![](/images/colour-saturation.png){.md45}
+
+
+</section>
+
diff --git a/notes/computing.md b/notes/computing.md
new file mode 100644
index 0000000..b1ab149
--- /dev/null
+++ b/notes/computing.md
@@ -0,0 +1,12 @@
+---
+title: computing
+published: 2024-07-22T15:34:49+0100
+children:
+ - functional
+ - operating-system
+ - software
+---
+
+## A computer is a machine that carries out a sequence of math and logic operations
+
+Computing involves the use of a computer to do a task. Machine code commands the computer, and is used to manipulate its data (memory, i/o, etc). A list of instructions that the computer runs is called a program.
diff --git a/notes/functional.md b/notes/functional.md
new file mode 100644
index 0000000..057b2a2
--- /dev/null
+++ b/notes/functional.md
@@ -0,0 +1,9 @@
+---
+title: functional programming
+published: 2024-07-22T15:34:49+0100
+children:
+ - lambda-calculus
+ - haskell
+---
+
+## A programming paradigm that models computation as the evaluation of expressions, and avoids mutable state
diff --git a/notes/haskell.md b/notes/haskell.md
new file mode 100644
index 0000000..a810a0e
--- /dev/null
+++ b/notes/haskell.md
@@ -0,0 +1,118 @@
+---
+title: haskell
+published: 2024-09-12T23:42:45+0100
+---
+
+## Static typing, pure functions, and lazy evaluation
+
+I use GHC2010 with a number of extra extensions, so code on this card may not be compliant with Haskell2010.
+
+## Index
+- [Generic types]
+- [Abstractions]
+ - [Semigroup]
+ - [Monoid: A Semigroup with an identity element]
+ - [Functor: A computation whose values can be mapped over]
+ - [Applicative: A functor with expression embedding and sequencing]
+ - [Monad: An applicative functor that can be flattened]
+- [Out]
+
+# Generic types
+Haskell has *parametric polymorphism*, meaning a function or datatype can handle multiple input types.
+```hs
+-- Polymorphic function with type variable `a'
+id ∷ a → a
+id x = x
+
+-- Also works with datatypes
+data Tree = Leaf | Node a (Tree a) (Tree a)
+```
+
+# Abstractions
+Haskell includes a lot of useful abstractions, some of which draw from **category theory**, a branch of maths. This might sound scary if you're unfamiliar, but you don't need to know category theory to know the language. These abstractions can be extremely useful, and without them, purely functional programming would be a pain in the ass!!
+
+## Semigroup
+An abstract representation of an object that has 1 associative binary operation. Yeah, that is it!
+```hs
+class Semigroup m where
+ (<>) ∷ m → m → m
+```
+
+Laws:
+```hs
+x <> (y <> z) == (x <> y) <> z
+```
+
+## Monoid: A semigroup with an identity element
+```hs
+class Semigroup m ⇒ Monoid m where
+ mempty ∷ m -- Identity element
+ mappend ∷ m → m → m -- Synonym for `<>'
+```
+
+Laws:
+```hs
+x <> mempty == x
+mempty <> x == x
+```
+
+## Functor: A computation whose values can be mapped over
+The functor **f β** is made from **f α** by transforming all of its values **(α → β)** while leaving the structure **f** itself unmodified.
+```hs
+class Functor f where
+ fmap ∷ (a → b) → f a → f b
+```
+
+Laws:
+```hs
+fmap id == id
+fmap (f . g) x == fmap f (fmap f x)
+```
+
+Extra methods:
+```hs
+(<$>) = fmap
+(<$) ∷ a → f b → f a
+($>) ∷ f a → b → f b
+void ∷ f a → f ()
+```
+
+## Applicative: A functor with expression embedding and sequencing
+```hs
+class Functor m ⇒ Applicative f where
+ (<*>) ∷ f (a → b) → f a → f b -- Sequencing
+ pure ∷ a → f a -- Expression embedding
+```
+
+Laws:
+```hs
+fmap f x == pure f <*> x
+
+pure id <*> v == x
+pure (.) <*> u <*> v <*> w == u <*> (v <*> w)
+u <*> pure y == pure ($ y) <*> u
+```
+
+## Monad: An applicative functor that can be flattened
+'>>=' could be named 'flatMap', as it's the equivelant of mapping **m α → m (m β)** and then flattening **m (m β) → m β**.
+
+```hs
+class Applicative m ⇒ Monad m where
+ (>>=) ∷ m a → (a → m b) → m b -- Flat map
+ (>>) ∷ m a → m b → m b -- Sequence and discard
+ return ∷ a → m a -- Synonym for `pure'
+```
+
+Laws:
+```hs
+pure a >>= f == f a
+m >>= pure == m
+(m >>= f) >>= g == m >>= (\x → f x >>= g)
+```
+
+# Out
+:::::{.links}
+- [mooc.fi's free online Haskell course](https://haskell.mooc.fi/)
+- [haskellbyexample](https://lotz84.github.io/haskellbyexample/)
+- [module organization guidelines for haskell projects](https://www.haskellforall.com/2021/05/module-organization-guidelines-for.html)
+:::::
diff --git a/notes/index.md b/notes/index.md
new file mode 100644
index 0000000..f503ac9
--- /dev/null
+++ b/notes/index.md
@@ -0,0 +1,18 @@
+---
+title: digital garden
+published: 2024-09-19T08:55:57+0100
+children:
+ - art
+ - computing
+ - kcp
+---
+
+This is a hierarchy of note cards that are connected together with hyperlinks, and organized for convenient navigation.
+The notes are intended to be brief, and contain links out to further resources.
+
+### History
+date comment
+------------- -----------------------------------
+20 Sep 2024 new website generator
+28 May 2024 markup → HTML compiler
+27 May 2024 first content
diff --git a/notes/kcp.md b/notes/kcp.md
new file mode 100644
index 0000000..e09e6c0
--- /dev/null
+++ b/notes/kcp.md
@@ -0,0 +1,12 @@
+---
+title: ~kcp
+published: 2024-09-26T00:13:02+0100
+children:
+ - sable
+---
+
+<p class="notice">
+This is a **section** that is used for notes regarding personal projects before documentation is properly made.
+</p>
+
+![Self-portrait](/images/kcp-boxed-in.jpg)
diff --git a/notes/lambda-calculus.md b/notes/lambda-calculus.md
new file mode 100644
index 0000000..98221eb
--- /dev/null
+++ b/notes/lambda-calculus.md
@@ -0,0 +1,29 @@
+---
+title: lambda calculus
+published: 2024-09-26T00:54:15+0100
+---
+
+:::::{.notice}
+**Lambda calculus** is a turing-complete system for expressing computations using only **[functions]** and **[application]**. Expressions can be one of a few elements:
+
+Syntax Description Example
+---------------- ------------ --------
+VAR Variable `x`
+(λVAR.expr) Function `λx.x`
+(FUNCTION expr) [Application] `(λx.x)a`
+
+Parentheses can be removed when it doesn't introduce ambiguity.
+:::::
+
+### Application
+Evaluation is done via substitution.
+
+When the expression `(λx.x)a` is evaluated, all occurences of **x** in the function's body are replaced with **a**. So, `(λx.x)a` evaluates to **a**.
+
+### Functions
+<details>
+<summary>The most basic function is the **identity function**, `λx.x`.</summary>
+<p>The function `λx.x` binds the variable "x" in the definition `λx` to the body `.x`. You can think of it like the function `f(x) = x`.</p>
+</details>
+
+
diff --git a/notes/music.md b/notes/music.md
new file mode 100644
index 0000000..45c2d65
--- /dev/null
+++ b/notes/music.md
@@ -0,0 +1,14 @@
+---
+title: music
+published: 2024-06-23T07:28:48+0100
+---
+
+## An art form that uses sound to create harmony, melody, rhythm, and structure
+
+### Key signatures
+
+It's easy to tell key signatures from sheet music; the **last sharp** is the **7th**, and the **last flat** is the **4th**.
+
+![](/images/music-key-signatures.webp)
+
+![© piano-music-theory.com](/images/music-circle-of-fifths.webp)
diff --git a/notes/operating-system.md b/notes/operating-system.md
new file mode 100644
index 0000000..16d8733
--- /dev/null
+++ b/notes/operating-system.md
@@ -0,0 +1,23 @@
+---
+title: operating system
+published: 2024-07-22T15:34:49+0100
+---
+
+## An operating system manages a computers's hardware and software resources
+
+### OpenBSD: Free, multi-platform, 4.4BSD-based, UNIX-like operating system
+:::::{.links}
+- [openbsd.org](https://www.openbsd.org/)
+- [man.openbsd.org](https://man.openbsd.org/)
+- [soure tree (github mirror)](https://github.com/openbsd/src)
+- [ports tree (github mirror)](https://github.com/openbsd/ports)
+:::::
+
+### Linux: Free, multi-platform, UNIX-like kernel
+Linux is a free kernel created by Linus Torvalds. The term "Linux" sometimes describes a UNIX-like operating systems that use the Linux kernel. Software in these operating systems is often provided by the GNU project, so "GNU/Linux" is sometimes used to refer to a distro.
+
+:::::{.links}
+- [kernel.org](https://kernel.org)
+- [major distributions](https://distrowatch.com/dwres.php?resource=major)
+- [Paging in Linux: understanding the linux kernel](https://www.oreilly.com/library/view/understanding-the-linux/0596000022/0596000022_ch02-77938.html)
+:::::
diff --git a/notes/perspective.md b/notes/perspective.md
new file mode 100644
index 0000000..fc492f8
--- /dev/null
+++ b/notes/perspective.md
@@ -0,0 +1,30 @@
+---
+title: point-projection perspective
+published: 2024-06-23T07:28:48+0100
+---
+
+<p class="notice">This is a technique used to represent 3d objects on 2d surfaces in a way that approximates human vision. It relies on the fact that objects become N times smaller as they get N times further from the eye.</p>
+
+<section>
+- [Horizon line]
+- [Vanishing point]
+ - [1 point]
+ - [2 points]
+</section>
+
+<section>
+## Horizon line
+![Roughly where the sky meets the land](/images/perspective-horizon.jpg)
+</section>
+
+<section>
+## Vanishing point
+A [vanishing point] is a place on the image where perspective projections of parallel lines in 3d space converge. On flat planes, vanishing points will always lie on the [horizon line]. Traditionally, illustrations will use 1-3 vanishing points.
+
+### 1 point
+![](/images/perspective-single-point.jpg)
+
+### 2 points
+![](/images/perspective-double-point.jpg)
+
+</section>
diff --git a/notes/renoise.md b/notes/renoise.md
new file mode 100644
index 0000000..c190ce7
--- /dev/null
+++ b/notes/renoise.md
@@ -0,0 +1,107 @@
+---
+title: renoise
+published: 2024-09-30T05:25:45+0100
+---
+
+<p class="notice">Renoise is proprietary software, but offers a generous [free trial].</p>
+<style>.inline{display:inline-block;padding-right:1rem;}</style>
+<section>
+
+- [Effect commands]
+ - [Global]
+ - [Samples only]
+ - [Any instrument]
+ - [Retrigger volume factor]
+ - [Volume column]
+ - [Panning column]
+
+</section>
+<section>
+
+## Effect commands
+[Renoise wiki](https://tutorials.renoise.com/wiki/Effect_Commands){.button}
+
+### Global
+
+command meaning comment
+-------- -------------------- --------
+`ZTxx` **T**empo in BPM
+`ZLxx` **L**ines per beat
+`ZKxx` Tic**k**s per line
+`ZGxx` Toggle **g**roove
+`ZBxx` **B**reak pattern goto **line xx** (?)
+`ZDxx` **D**elay pattern pause for **xx lines**
+
+### Samples only
+These work in the local FX columns for samples.
+
+command meaning comment
+-------- --------------------- --------
+`-Axy` **A**rpeggio x,y **first, second semitone**
+`-Uxx` Slide pitch **u**p xx **1/16th of semitone**
+`-Dxx` Slide pitch **d**own xx **1/16th of semitone**
+`-Gxx` **G**lide xx **1/16th of semitone**
+`-Vsd` **V**ibrato s,d **speed, depth**
+`-Ixx` Fade **i**n xx **1/256th of volume**
+`-Oxx` Fade **o**ut xx **1/256th of volume**
+`-Tsd` **T**remelo s,d **speed,depth**
+`-Cxt` **C**ut volume=**x** after **t** ticks
+`-Sxx` **S**lice number
+`-Bxx` **B**ackwards
+`-Exx` **E**nvelope offset set envelope, ahdsr, fader, & stepper offset
+`-Nsd` Auto pa**n** s,d **speed,depth**
+
+### Any instrument
+These work in the local FX columns for any instrument.
+
+command meaning comment
+-------- -------------------- --------
+`-Mxx` Channel volu**m**e `00` = -60dB, `FF` = +3dB
+`-Qxx` Delay playback Delay playback **xx ticks**
+`-Yxx` Ma**y**be retrigger Trigger with **xx probability**
+`-Rxt` **R**etrigger [Retrigger volume factor] every **t**ick
+
+#### Retrigger volume factor
+:::::{.inline}
+x change in volume
+--- -----------------
+`0` No change
+`1` 3% lower
+`2` 6% lower
+`3` 12% lower
+`4` 25% lower
+`5` 50% lower
+`6` 33% lower cumulatively
+`7` 50% lower cumulatively
+:::::
+
+:::::{.inline}
+x change in volume
+--- ----------------
+`8` No change
+`9` 3% higher
+`a` 6% higher
+`b` 12% higher
+`c` 25% higher
+`d` 50% higher
+`e` 33% higher cumulatively
+`f` 50% higher cumulatively
+:::::
+
+### Volume column
+command meaning comment
+---------- -------------------- --------
+`00`-`7f` Volume/velocity
+`Ix` Volume fade in step **x * 10**
+`Ox` Volume fade out step **x * 10**
+
+### Panning column
+command meaning comment
+---------- -------------------- --------
+`00`-`80` Panning 00 = left, 40 = centre, 80 = right
+`Jx` Slide left
+`Kx` Slide right
+
+</section>
+
+[free trial]: https://www.renoise.com/download
diff --git a/notes/sable.md b/notes/sable.md
new file mode 100644
index 0000000..ca1915f
--- /dev/null
+++ b/notes/sable.md
@@ -0,0 +1,32 @@
+---
+title: sable
+published: 2024-09-23T22:12:19+0100
+---
+
+<section>
+## Strictly evaluated, purely functional programming
+
+- [Syntax]
+ - [Comments]
+ - [Literals]
+ - [Definitions]
+</section>
+
+<section>
+## Syntax
+
+### Comments
+```
+-- There are only line comments for neow
+```
+
+### Literals
+```
+true: bool
+false: bool
+123: int
+\x: char
+
+
+</section>
+<!-- todo any documentation for the compiler -->
diff --git a/notes/scales.md b/notes/scales.md
new file mode 100644
index 0000000..9c45d95
--- /dev/null
+++ b/notes/scales.md
@@ -0,0 +1,23 @@
+---
+title: scales
+published: 2024-10-02T05:46:22+0100
+---
+<section>
+
+- [Western scales]
+ - [Diatonic]
+ - [Harmonic minor]
+ - [Melodic minor]
+
+</section>
+<section>
+
+## Western scales
+
+### Diatonic
+
+### Harmonic minor
+
+### Melodic minor
+
+</section>
diff --git a/notes/software.md b/notes/software.md
new file mode 100644
index 0000000..a29bb9b
--- /dev/null
+++ b/notes/software.md
@@ -0,0 +1,42 @@
+---
+title: software
+published: 2024-07-22T15:34:49+0100
+children:
+ - renoise
+---
+
+<p class="notice">
+This is a collection of (mostly free) software that runs natively on x86-64 linux.
+</p>
+
+<section>
+- Free
+ - [Audio plugins]
+ - [Video]
+ - [Visual media]
+- [Proprietary]
+</section>
+
+<section>
+### Audio plugins
+- [CHOW Tape](https://github.com/jatinchowdhury18/AnalogTapeModel) - analog tape model
+- [Surge XT](https://surge-synthesizer.github.io/) - hybrid synthesizer
+ - [Why Surge XT is a fantastic free synth... and how to use it](https://www.youtube.com/watch?v=oeamVu1qY-g)
+- [RaveGenerator 2](https://blog.wavosaur.com/rave-generator-2-vst-audiounit-the-stab-machine-is-back-in-the-house/) - stab machine
+
+### Video
+- [OBS](https://obsproject.com) - record videos or live stream
+- [Olive](https://www.olivevideoeditor.org) - configurable video editor
+- [Shotcut](https://www.shotcut.org) - video editor
+
+### Visual media
+- [Krita](https://krita.org/en/) - painting and photo editing
+- [Opentoonz](https://opentoonz.github.io/e) - 2d animation software
+ - [Tahoma2d](https://tahoma2d.org) - fork of Opentoonz, supposedly with a simpler interface
+</section>
+
+<section>
+## Proprietary
+- [Renoise](https://renoise.com) - a digital audio workstation
+- [TVPaint](https://tvpaint.com/en) - 2d animation software
+</section>
diff --git a/notes/visual-art.md b/notes/visual-art.md
new file mode 100644
index 0000000..05485d5
--- /dev/null
+++ b/notes/visual-art.md
@@ -0,0 +1,27 @@
+---
+title: visual art
+published: 2024-06-23T07:28:48+0100
+children:
+ - colour
+ - perspective
+---
+
+## A diverse class of art forms that utilize imagery
+
+### Animation
+
+A technique used to create the illusion of movement from still images.
+Disney's twelve principles:
+
+- Squash and stretch
+- Anticipation
+- Staging
+- Straight ahead action OR pose to pose
+- Follow through and overlapping action
+- Slow in and slow out
+- Arc
+- Secondary action
+- Timing
+- Exaggeration
+- Solid drawing
+- Appeal
diff --git a/site.cabal b/site.cabal
new file mode 100644
index 0000000..bcc4a59
--- /dev/null
+++ b/site.cabal
@@ -0,0 +1,36 @@
+cabal-version: 2.2
+name: site
+version: 0.1.0
+build-type: Simple
+
+executable site
+ hs-source-dirs: src
+ main-is: Main.hs
+ ghc-options: -threaded -rtsopts -with-rtsopts=-N
+ default-language: GHC2021
+ default-extensions:
+ ApplicativeDo
+ , BlockArguments
+ , OverloadedLists
+ , OverloadedStrings
+ , LambdaCase
+ , MultiWayIf
+ , PatternSynonyms
+ , TemplateHaskell
+ , TypeFamilies
+ , UnicodeSyntax
+ , ViewPatterns
+ build-depends:
+ base == 4.*
+ , hakyll == 4.16.*
+ , time == 1.*
+ , clay == 0.15.*
+ , relude >= 1.1.0.0
+ -- , web-view == 0.4.*
+ , binary
+ , pandoc
+ , pandoc-types
+ mixins:
+ base hiding (Prelude)
+ , relude (Relude as Prelude)
+ , relude
diff --git a/src/Main.hs b/src/Main.hs
new file mode 100644
index 0000000..365ae48
--- /dev/null
+++ b/src/Main.hs
@@ -0,0 +1,201 @@
+module Main where
+
+import Hakyll hiding (fromList)
+import Hakyll qualified
+
+import Data.Binary (Binary)
+import Data.String (IsString (fromString))
+import Data.Time (UTCTime, defaultTimeLocale, parseTimeM)
+
+import Data.Map.Lazy qualified as Map
+
+main ∷ IO ()
+main = hakyll $ do
+ match "css/*.css" copyAll
+ match "images/**" copyAll
+
+ match "css/*.hs" do
+ route toCss
+ compile $ getResourceString >>= withItemBody (unixFilter "runghc" [])
+
+ match "src/*.hs" $
+ compile getResourceString
+
+ match "templates/*" $
+ compile templateBodyCompiler
+
+ cards ← buildCards "notes/index.md"
+ let dep = cardsDependency cards
+ let ids = cardId <$> cardsList cards
+ let all = Hakyll.fromList ids
+ rulesExtraDependencies [dep] $ match all do
+ route toHtml
+ compile $ do
+ card ← getUnderlying >>= cardsGet cards
+ nav ← cardNav cards card
+ pandocCompiler
+ <&> fmap (nav <>)
+ >>= loadAndApplyTemplate "templates/card.html" pageCtx
+ >>= loadAndApplyTemplate "templates/default.html" pageCtx
+ >>= relativizeUrls
+
+ match "pages/*.md" $ do
+ route $ gsubRoute "pages/" (const "") `composeRoutes` toHtml
+ let top5 key sortBy = loadAll all >>= sortBy <&> take 5 . reverse
+ let genCtx key = listField key pageCtx . top5 key
+ compile . pageCompiler "templates/default.html" $
+ pageCtx
+ <> genCtx "cardsModified" byModified
+ <> genCtx "cardsPublished" byPublished
+
+pageCtx ∷ Context String
+pageCtx =
+ defaultContext
+ <> snippetField
+ <> dateField "date" dateFormat
+ <> modificationTimeField "dateModified" dateFormat
+
+cardNav ∷ Cards → Card → Compiler String
+cardNav cards self = do
+ let selfId = cardId self
+ let parentId = cardParent self
+
+ children ← navChildren cards "" self
+
+ siblings ←
+ if parentId == cardIdNone
+ then pure ""
+ else cardsGet cards parentId >>= navChildren cards selfId
+
+ aunties ←
+ if parentId == cardIdNone
+ then pure ""
+ else do
+ parent ← cardsGet cards parentId
+ let grandparent = cardParent parent
+ if grandparent == cardIdNone
+ then pure ""
+ else cardsGet cards grandparent >>= navChildren cards parentId
+
+ pure $
+ "<header>\n"
+ <> ("<h1>" <> cardTitle self <> "</h1>\n")
+ <> aunties
+ <> siblings
+ <> children
+ <> "</header>"
+ where
+ wrap s = "<nav><ul>" <> s <> "</ul></nav>\n"
+ navChildren cards cur self =
+ wrap . htmlOf cur <$> routeChildren cards self
+ htmlOf cur routed = mconcat $ htmlOfOne cur <$> routed
+ htmlOfOne cur (card, path) =
+ "<li><a href=\"/"
+ <> path
+ <> "\""
+ <> (if cur == cardId card then "class=\"current\"" else "")
+ <> ">"
+ <> cardTitle card
+ <> "</a></li>\n"
+
+routeChildren ∷ Cards → Card → Compiler [(Card, FilePath)]
+routeChildren cards self = do
+ let children = cardChildren self
+ childrenCards ← cardsGet cards `mapM` children
+ childrenRoutes ← cardDotHtml `mapM` children
+ pure $ zip childrenCards childrenRoutes
+
+{-parent ← cardsGet cards $ cardParent self
+grandparent ← cardsGet cards $ cardParent parent
+
+aunties ← cardsGet cards `mapM` cardChildren grandparent
+siblings ← cardsGet cards `mapM` cardChildren parent-}
+
+buildCards ∷ (MonadFail m, MonadMetadata m) ⇒ CardId → m Cards
+buildCards id = do
+ cardMap ← buildCardMap cardIdNone id
+ dependencies ← makePatternDependency "notes/*.md"
+ pure (cardMap, dependencies)
+
+buildCardMap ∷ (MonadFail m, MonadMetadata m) ⇒ CardId → CardId → m CardMap
+buildCardMap parent id = do
+ meta ← getMetadata id
+ title ← getMetadataField' id "title"
+ let children = cardFromSymbol <$> fromMaybe [] (lookupStringList "children" meta)
+ let card = Card id parent children title
+ (<>) [(id, card)] . Map.unions <$> forM children (buildCardMap id)
+
+cardFromSymbol ∷ ToString α ⇒ α → CardId
+cardFromSymbol = fromString . (<> ".md") . ("notes/" <>) . toString
+
+cardDotHtml ∷ CardId → Compiler FilePath
+cardDotHtml id =
+ getRoute id >>= \case
+ Just x → pure x
+ Nothing → fail "Card must have a route"
+
+dateFormat ∷ String
+dateFormat = "%e %b %Y %H:%M:%S"
+
+pageCompiler ∷ Identifier → Context String → Compiler (Item String)
+pageCompiler template ctx =
+ getResourceString
+ >>= applyAsTemplate ctx
+ >>= renderPandoc
+ >>= loadAndApplyTemplate template ctx
+ >>= relativizeUrls
+
+copyAll ∷ Rules ()
+copyAll = route idRoute >> compile copyFileCompiler
+
+toHtml, toCss ∷ Routes
+toHtml = setExtension "html"
+toCss = setExtension "css"
+
+byModified, byPublished ∷ [Item a] → Compiler [Item a]
+byModified = sortByM modified
+byPublished = sortByM published
+
+modified, published ∷ Item a → Compiler UTCTime
+modified = getItemModificationTime . itemIdentifier
+published = getItemUTC defaultTimeLocale . itemIdentifier
+
+getDateField ∷ (MonadMetadata m, MonadFail m) ⇒ String → Item a → m UTCTime
+getDateField key x = do
+ field ← getMetadataField (itemIdentifier x) key
+ case field of
+ Just str →
+ parseTimeM True defaultTimeLocale dateFormat str
+ Nothing →
+ fail $ "Couldn't get field " ++ key ++ " for " ++ show (itemIdentifier x)
+
+cardIdNone ∷ CardId
+cardIdNone = ""
+
+type CardId = Identifier
+
+data Card = Card
+ { cardId ∷ CardId
+ , cardParent ∷ CardId
+ , cardChildren ∷ [CardId]
+ , cardTitle ∷ String
+ }
+
+cardsMap ∷ Cards → CardMap
+cardsDependency ∷ Cards → Dependency
+cardsList ∷ Cards → [Card]
+cardsGet ∷ Cards → CardId → Compiler Card
+
+cardsDependency (_, d) = d
+cardsMap (m, _) = m
+cardsList = Map.elems . cardsMap
+cardsGet cards id =
+ case cardsMap cards Map.!? id of
+ Just card → pure card
+ Nothing → fail "???"
+
+type Cards = (CardMap, Dependency)
+type CardMap = Map CardId Card
+
+sortByM ∷ (Monad m, Ord k) ⇒ (a → m k) → [a] → m [a]
+sortByM f xs = map fst . sortBy (comparing snd) <$> mapM (liftA2 fmap (,) f) xs
diff --git a/templates/card.html b/templates/card.html
new file mode 100644
index 0000000..3528c51
--- /dev/null
+++ b/templates/card.html
@@ -0,0 +1,20 @@
+<style type="text/css">
+.timestamp {
+ color: var(--text-light);
+ font-size: 0.9rem;
+}
+aside {
+ max-width: 40%;
+ width: fit-content;
+ padding: 1rem;
+}
+</style>
+
+<aside>
+ <div class="timestamp">Created at $date$</div>
+ <div class="timestamp">Modified at $dateModified$</div>
+</aside>
+
+<article class="card">
+$body$
+</article>
diff --git a/templates/default.html b/templates/default.html
new file mode 100644
index 0000000..9933f5c
--- /dev/null
+++ b/templates/default.html
@@ -0,0 +1,65 @@
+<!DOCTYPE html>
+<html lang="en">
+
+<head>
+ <meta charset="utf-8">
+ <meta http-equiv="x-ua-compatible" content="ie=edge">
+ <meta name="viewport" content="width=device-width, initial-scale=1">
+ <title>$title$</title>
+ <!--<base href="https://bsd.computer/~kcp/index.html">-->
+ <link href="/css/kate.css" rel="stylesheet">
+ <link rel="stylesheet" href="https://cdn.simplecss.org/simple.min.css">
+ <link href="/css/default.css" rel="stylesheet">
+ <link href="/css/cursor.css" rel="stylesheet">
+</head>
+
+<body>
+ <header>
+ <nav><ul><a href="/">home</a><a href="/notes.html">garden</a></ul></nav>
+ </header>
+
+ <main role="main">
+$body$
+ </main>
+
+ <footer>
+ <a href="https://kitty.codeberg.page">
+ <img src="/images/button/paws.gif" loading="lazy"
+ alt="Made with MY OWN TWO PAWS"
+ title="Made with my own two paws"></a>
+
+ <a href="/source.html">
+ <img src="/images/button/haskell.gif" loading="lazy"
+ alt="Powered by haskell"
+ title="Powered by haskell"></a>
+
+ <a href="mailto:kcp@bsd.computer">
+ <img src="/images/button/email.gif" loading="lazy"
+ alt="Questions or comments? E-mail me!"
+ title="Questions or comments? E-mail me!"></a>
+
+ <a rel="license noopener noreferrer" href="https://creativecommons.org/licenses/by-nc-sa/4.0/" target="_blank">
+ <img src="https://licensebuttons.net/i/l/by-nc-sa/eeeeee/ff/66/99/88x31.png"
+ alt="CC BY-NC-SA 4.0"
+ title="All content on this page is CC BY-NC-SA 4.0 unless otherwise indicated"
+ loading="lazy"></a>
+
+ <p typeof="cc:Work"
+ xmlns:dct="http://purl.org/dc/terms/"
+ xmlns:cc="http://creativecommons.org/ns#">
+ <b property="dct:title">kitty piapiac's website</b>
+ &copy;
+ <span property="dct:dateCopyrighted">2024</span>
+ <a property="cc:attributionName"
+ rel="cc:attributionUrl dct:creator"
+ target="_blank" href="https://purl.org/kcp">
+ ~kcp</a>,
+ <a rel="license"
+ target="_blank" href="https://creativecommons.org/licenses/by-nc-sa/4.0/">
+ CC BY-NC-SA 4.0</a>
+ <span>unless otherwise indicated</span>
+ </p>
+ </footer>
+</body>
+
+</html>
diff --git a/templates/recently-modified.md b/templates/recently-modified.md
new file mode 100644
index 0000000..70bd495
--- /dev/null
+++ b/templates/recently-modified.md
@@ -0,0 +1,2 @@
+$for(cardsModified)$- [$title$]($url$) <span class="timestamp">at $dateModified$</span>
+$endfor$
diff --git a/templates/recently-published.md b/templates/recently-published.md
new file mode 100644
index 0000000..018270f
--- /dev/null
+++ b/templates/recently-published.md
@@ -0,0 +1,2 @@
+$for(cardsPublished)$- [$title$]($url$) <span class="timestamp">at $date$</span>
+$endfor$