Author:ptrace
Comitter:ptrace
Date:2026-06-17 00:58:25 UTC
diff --git a/.gitignore b/.gitignore
index 500b378..94dd56f 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,5 +1,5 @@
build/
images/*.jpg
*.tar.gz
deploy.sh
deploy.bat
build/
images/*.jpg
*.tar.gz
deploy.sh
deploy.bat
diff --git a/images/sticher.py b/images/sticher.py
index 2402674..76b149d 100644
--- a/images/sticher.py
+++ b/images/sticher.py
@@ -1,42 +1,42 @@
import os
from pathlib import Path
from PIL import Image
class Stich:
def __init__(self):
self.input_folder = Path('pngs')
self.output_fn = 'no_ads'
self.width = 160
self.height = 50
def run(self):
images = self.get_images()
self.stich(images)
def get_images(self):
image_files = sorted([f for f in os.listdir(self.input_folder) if f.lower().endswith('.png')])
images = [Image.open(os.path.join(self.input_folder, fname)) for fname in image_files]
return images
def stich(self, images):
for img in images:
if img.size != (self.width, self.height):
raise ValueError(f'Image {img.filename} is not {self.width}x{self.height}px')
total_width = self.width * len(images)
stitched_image = Image.new('RGB', (total_width, self.height))
for i, img in enumerate(images):
stitched_image.paste(img, (i * self.width, 0))
output_path = Path(self.output_fn + '.jpg')
stitched_image.save(output_path, 'JPEG', quality=100)
print(f'Saved stitched image to {output_path}')
if __name__ == '__main__':
Stich().run()
import os
from pathlib import Path
from PIL import Image
class Stich:
def __init__(self):
self.input_folder = Path('pngs')
self.output_fn = 'no_ads'
self.width = 160
self.height = 50
def run(self):
images = self.get_images()
self.stich(images)
def get_images(self):
image_files = sorted([f for f in os.listdir(self.input_folder) if f.lower().endswith('.png')])
images = [Image.open(os.path.join(self.input_folder, fname)) for fname in image_files]
return images
def stich(self, images):
for img in images:
if img.size != (self.width, self.height):
raise ValueError(f'Image {img.filename} is not {self.width}x{self.height}px')
total_width = self.width * len(images)
stitched_image = Image.new('RGB', (total_width, self.height))
for i, img in enumerate(images):
stitched_image.paste(img, (i * self.width, 0))
output_path = Path(self.output_fn + '.jpg')
stitched_image.save(output_path, 'JPEG', quality=100)
print(f'Saved stitched image to {output_path}')
if __name__ == '__main__':
Stich().run()
diff --git a/makefile b/makefile
index 1ae9ac2..526e346 100644
--- a/makefile
+++ b/makefile
@@ -1,23 +1,23 @@
.PHONY: all build run bundle
all: build run
ARCHIVE_FILE = guksu_website.tar.gz
build:
@if [ ! -d "build" ]; then mkdir build; fi
gcc src/gen.c -g -o build/gen
run:
build/gen
bundle:
@echo "Bundling Project and archiving it ..."
tar -czf ./$(ARCHIVE_FILE) www/*
clean:
@rm -r build
@rm $(ARCHIVE_FILE)
@echo "Removed builds and temp files"
.PHONY: all build run bundle
all: build run
ARCHIVE_FILE = guksu_website.tar.gz
build:
@if [ ! -d "build" ]; then mkdir build; fi
gcc src/gen.c -g -o build/gen
run:
build/gen
bundle:
@echo "Bundling Project and archiving it ..."
tar -czf ./$(ARCHIVE_FILE) www/*
clean:
@rm -r build
@rm $(ARCHIVE_FILE)
@echo "Removed builds and temp files"
diff --git a/src/gen.c b/src/gen.c
index 57f2aca..ca51392 100644
--- a/src/gen.c
+++ b/src/gen.c
@@ -3,6 +3,7 @@
int main() {
printf("Hello");
printf("Hello\n");
return 0;
}
diff --git a/www/data.js b/www/data.js
index dca5fce..f93f9a5 100644
--- a/www/data.js
+++ b/www/data.js
@@ -1,88 +1,88 @@
const data = {
entries: [
{
name: "Fefe's Blog",
category: "tech",
url: "https://blog.fefe.de",
description: "Blogs about many different things, but mostly about code, tech and politics. Language: German"
},
{
name: "Tsoding",
category: "tech",
url: "https://www.youtube.com/@TsodingDaily",
description: "Recreational programmer. Streams about many different programming topics"
},
{
name: "Penger",
category: "art",
url: "https://penger.city/",
description: "Community mascot of Tsoding"
},
{
name: "smile.rip",
category: "art",
url: "https://www.smile.rip",
description: "Can't describe it. It just looks awesome"
},
{
name: "Tonsky",
category: "tech",
url: "https://tonsky.me/",
description: "Developer and UI/UX designer"
},
{
name: "Casey Muratori",
category: "tech",
url: "https://caseymuratori.com/",
description: "Casey dives into CPU architectures and low level stuff in programming languages"
},
{
name: "Isotopp",
category: "tech",
url: "https://blog.koehntopp.info/",
description: "Blogs about programming, tech, climate change, and miscellaneous stuff. Language German/English"
},
{
name: "Linux Syscall Table (x86_64)",
category: "programming",
url: "https://filippo.io/linux-syscall-table/",
description: "Searchable Linux Syscall Table for x86_64 assembly"
},
{
name: "the-hub-of-heliopolis",
category: "tech",
url: "https://p403n1x87.github.io/",
description: "Blogs about programming, IoT and tech in general"
},
{
name: "GDB Cheat Sheet",
category: "programming",
url: "https://darkdust.net/files/GDB%20Cheat%20Sheet.pdf",
description: "GDB cheatsheet (pdf)"
},
{
name: "holz.nu",
category: "tech",
url: "https://blog.holz.nu/",
description: "Link collection/blog about tech and programming. Language: German/English"
},
{
name: "Louis Rossmann",
category: "tech",
url: "https://www.youtube.com/@rossmanngroup",
description: "Fights for right-of-repair and talks about greedy/scammy companies and their questionable practises"
},
{
name: "Consumers Rights Wiki",
category: "misc",
url: "https://consumerrights.wiki/Main_Page",
description: "A database of consumer explotation practises. Contribution is very welcome!"
},
{
"name": "solarium.technology",
"category": "tech",
"url": "https://solarium.technology",
"description": "Tech related site, which also aims for a broad compatibility with many, many browsers"
},
]
}
const data = {
entries: [
{
name: "Fefe's Blog",
category: "tech",
url: "https://blog.fefe.de",
description: "Blogs about many different things, but mostly about code, tech and politics. Language: German"
},
{
name: "Tsoding",
category: "tech",
url: "https://www.youtube.com/@TsodingDaily",
description: "Recreational programmer. Streams about many different programming topics"
},
{
name: "Penger",
category: "art",
url: "https://penger.city/",
description: "Community mascot of Tsoding"
},
{
name: "smile.rip",
category: "art",
url: "https://www.smile.rip",
description: "Can't describe it. It just looks awesome"
},
{
name: "Tonsky",
category: "tech",
url: "https://tonsky.me/",
description: "Developer and UI/UX designer"
},
{
name: "Casey Muratori",
category: "tech",
url: "https://caseymuratori.com/",
description: "Casey dives into CPU architectures and low level stuff in programming languages"
},
{
name: "Isotopp",
category: "tech",
url: "https://blog.koehntopp.info/",
description: "Blogs about programming, tech, climate change, and miscellaneous stuff. Language German/English"
},
{
name: "Linux Syscall Table (x86_64)",
category: "programming",
url: "https://filippo.io/linux-syscall-table/",
description: "Searchable Linux Syscall Table for x86_64 assembly"
},
{
name: "the-hub-of-heliopolis",
category: "tech",
url: "https://p403n1x87.github.io/",
description: "Blogs about programming, IoT and tech in general"
},
{
name: "GDB Cheat Sheet",
category: "programming",
url: "https://darkdust.net/files/GDB%20Cheat%20Sheet.pdf",
description: "GDB cheatsheet (pdf)"
},
{
name: "holz.nu",
category: "tech",
url: "https://blog.holz.nu/",
description: "Link collection/blog about tech and programming. Language: German/English"
},
{
name: "Louis Rossmann",
category: "tech",
url: "https://www.youtube.com/@rossmanngroup",
description: "Fights for right-of-repair and talks about greedy/scammy companies and their questionable practises"
},
{
name: "Consumers Rights Wiki",
category: "misc",
url: "https://consumerrights.wiki/Main_Page",
description: "A database of consumer explotation practises. Contribution is very welcome!"
},
{
"name": "solarium.technology",
"category": "tech",
"url": "https://solarium.technology",
"description": "Tech related site, which also aims for a broad compatibility with many, many browsers"
},
]
}
diff --git a/www/data.json b/www/data.json
index 358907e..4f3b432 100644
--- a/www/data.json
+++ b/www/data.json
@@ -1,86 +1,86 @@
[
{
"name": "Fefe's Blog",
"category": "tech",
"url": "https://blog.fefe.de",
"description": "Blogs about many different things, but mostly about code, tech and politics. Language: German"
},
{
"name": "Tsoding",
"category": "tech",
"url": "https://www.youtube.com/@TsodingDaily",
"description": "Recreational programmer. Streams about many different programming topics"
},
{
"name": "Penger",
"category": "art",
"url": "https://penger.city/",
"description": "Community mascot of Tsoding"
},
{
"name": "smile.rip",
"category": "art",
"url": "https://www.smile.rip",
"description": "Can't describe it. It just looks awesome"
},
{
"name": "Tonsky",
"category": "tech",
"url": "https://tonsky.me/",
"description": "Developer and UI/UX designer"
},
{
"name": "Casey Muratori",
"category": "tech",
"url": "https://caseymuratori.com/",
"description": "Casey dives into CPU architectures and low level stuff in programming languages"
},
{
"name": "Isotopp",
"category": "tech",
"url": "https://blog.koehntopp.info/",
"description": "Blogs about programming, tech, climate change, and miscellaneous stuff. Language German/English"
},
{
"name": "Linux Syscall Table (x86_64)",
"category": "programming",
"url": "https://filippo.io/linux-syscall-table/",
"description": "Searchable Linux Syscall Table for x86_64 assembly"
},
{
"name": "the-hub-of-heliopolis",
"category": "tech",
"url": "https://p403n1x87.github.io/",
"description": "Blogs about programming, IoT and tech in general"
},
{
"name": "GDB Cheat Sheet",
"category": "programming",
"url": "https://darkdust.net/files/GDB%20Cheat%20Sheet.pdf",
"description": "GDB cheatsheet (pdf)"
},
{
"name": "holz.nu",
"category": "tech",
"url": "https://blog.holz.nu/",
"description": "Link collection/blog about tech and programming. Language: German/English"
},
{
"name": "Louis Rossmann",
"category": "tech",
"url": "https://www.youtube.com/@rossmanngroup",
"description": "Fights for right-of-repair and talks about greedy/scammy companies and their questionable practises"
},
{
"name": "Consumers Rights Wiki",
"category": "misc",
"url": "https://consumerrights.wiki/Main_Page",
"description": "A database of consumer explotation practises. Contribution is very welcome!"
},
{
"name": "solarium.technology",
"category": "tech",
"url": "https://solarium.technology",
"description": "Tech related site, which also aims for a broad compatibility with many, many browsers"
}
]
[
{
"name": "Fefe's Blog",
"category": "tech",
"url": "https://blog.fefe.de",
"description": "Blogs about many different things, but mostly about code, tech and politics. Language: German"
},
{
"name": "Tsoding",
"category": "tech",
"url": "https://www.youtube.com/@TsodingDaily",
"description": "Recreational programmer. Streams about many different programming topics"
},
{
"name": "Penger",
"category": "art",
"url": "https://penger.city/",
"description": "Community mascot of Tsoding"
},
{
"name": "smile.rip",
"category": "art",
"url": "https://www.smile.rip",
"description": "Can't describe it. It just looks awesome"
},
{
"name": "Tonsky",
"category": "tech",
"url": "https://tonsky.me/",
"description": "Developer and UI/UX designer"
},
{
"name": "Casey Muratori",
"category": "tech",
"url": "https://caseymuratori.com/",
"description": "Casey dives into CPU architectures and low level stuff in programming languages"
},
{
"name": "Isotopp",
"category": "tech",
"url": "https://blog.koehntopp.info/",
"description": "Blogs about programming, tech, climate change, and miscellaneous stuff. Language German/English"
},
{
"name": "Linux Syscall Table (x86_64)",
"category": "programming",
"url": "https://filippo.io/linux-syscall-table/",
"description": "Searchable Linux Syscall Table for x86_64 assembly"
},
{
"name": "the-hub-of-heliopolis",
"category": "tech",
"url": "https://p403n1x87.github.io/",
"description": "Blogs about programming, IoT and tech in general"
},
{
"name": "GDB Cheat Sheet",
"category": "programming",
"url": "https://darkdust.net/files/GDB%20Cheat%20Sheet.pdf",
"description": "GDB cheatsheet (pdf)"
},
{
"name": "holz.nu",
"category": "tech",
"url": "https://blog.holz.nu/",
"description": "Link collection/blog about tech and programming. Language: German/English"
},
{
"name": "Louis Rossmann",
"category": "tech",
"url": "https://www.youtube.com/@rossmanngroup",
"description": "Fights for right-of-repair and talks about greedy/scammy companies and their questionable practises"
},
{
"name": "Consumers Rights Wiki",
"category": "misc",
"url": "https://consumerrights.wiki/Main_Page",
"description": "A database of consumer explotation practises. Contribution is very welcome!"
},
{
"name": "solarium.technology",
"category": "tech",
"url": "https://solarium.technology",
"description": "Tech related site, which also aims for a broad compatibility with many, many browsers"
}
]
diff --git a/www/index.html b/www/index.html
index 099398e..7f1a7ad 100644
--- a/www/index.html
+++ b/www/index.html
@@ -1,1173 +1,1173 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<link rel="icon" type="image/x-icon" href="img/guksu_logo.png">
<title>guksu - link collection</title>
<style>
:root {
--t-title-anim-duration: 5s;
--no-ads-scroll-speed: 180s;
--link-color: green;
--link-hover: red;
--link-active: yellow;
--bg-color: #030304;
/* 80s colors */
--clr-red1: hsl(349, 71%, 70%);
--clr-red2: hsl(345, 73%, 32%);
--clr-red3: hsl(345, 90%, 60%);
--clr-blue1: hsl(199, 58%, 53%);
--clr-blue2: hsl(191, 76%, 71%);
--clr-green-dark: hsl(160, 99%, 10%);
--clr-green0: hsl(160, 99%, 40%);
--clr-green1: hsl(160, 99%, 53%);
--clr-green2: hsl(160, 99%, 70%);
--clr-green3: hsl(160, 99%, 82%);
--clr-orange1: hsl(28, 68%, 42%);
--clr-yellow1: hsl(35, 79%, 65%);
--clr-purple1: hsl(317, 73%, 47%);
--clr-purple2: hsl(320, 100%, 52%);
/* pixels */
--title-offset: 2px;
}
body {
margin: 0;
background-color: var(--bg-color);
color: #e9e9fc;
color-scheme: dark;
font-family: Verdana, Geneva, Tahoma, sans-serif;
}
h2 {
color: var(--clr-blue2);
text-shadow: 0 0 12px var(--clr-blue1);
margin-top: 1.2em;
padding-bottom: 0.5em;
border-bottom: solid 1px var(--clr-blue2);
}
.main-wrapper {
display: flex;
flex-flow: column nowrap;
height: 100vh;
}
.screen-stripes {
z-index: 19;
position: absolute;
width: 100vw;
height: 100vh;
pointer-events: none;
&::before {
content: "";
background: repeating-linear-gradient(
var(--clr-green2),
var(--clr-green2) 1px,
transparent 1px,
transparent 3px
);
opacity: 0.04;
top: 0;
left: 0;
position: absolute;
width: 100%;
height: 100%;
}
}
.screen-grain {
z-index: 20;
position: absolute;
width: 100vw;
height: 100vh;
pointer-events: none;
&::before {
content: "";
background-color: hsl(191, 76%, 34%);
background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 600 600'%3E%3Cfilter id='a'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='.65' numOctaves='3' stitchTiles='stitch'/%3E%3C/filter%3E%3Crect width='100%25' height='100%25' filter='url(%23a)'/%3E%3C/svg%3E");
background-repeat: repeat;
background-size: 182px;
opacity: 0.07;
top: 0;
left: 0;
position: absolute;
width: 100%;
height: 100%;
}
}
.make-it-bigger {
display: flex;
flex-flow: column wrap;
z-index: 15;
position: absolute;
top: 0;
right: 0;
width: 2px;
height: 2px;
background-color: #000;
cursor: zoom-in;
user-select: none;
animation: mib-shine 15s linear infinite;
align-items: center;
justify-content: center;
font-size: 1.4em;
color: var(--clr-green2);
& p {
padding: 1em;
margin: 0.2em;
}
}
@keyframes mib-shine {
0% { background-color: transparent; }
98% { background-color: transparent; }
99% { background-color: var(--clr-green3); }
100% { background-color: transparent; }
}
.blinky::after {
content: "▌";
opacity: 1;
color: var(--clr-green2);
padding-left: 0.3em;
animation: blinky-blink 1.25s linear infinite;
}
@keyframes blinky-blink {
0% { opacity: 1; }
49% { opacity: 1; }
49.9% { opacity: 0; }
99.9% { opacity: 0; }
100% { opacity: 1; }
}
.nav {
display: flex;
flex-flow: row wrap;
font-size: 1.0rem;
padding: 0.5em;
}
.nav ul, .menu ul {
margin: 0;
list-style: none;
padding-left: 0;
}
.nav li, .menu li {
float: left;
list-style: none;
}
.nav::before {
color: var(--clr-green1);
content: "Categories:";
animation: nav-cat-shadow 9s linear infinite;
}
@keyframes nav-cat-shadow {
0% { text-shadow: 1px 1px 30px var(--clr-blue2); }
30% { text-shadow: 1px 1px 30px var(--clr-blue2); }
50% { text-shadow: 1px 1px 80px var(--clr-blue2); }
60% { text-shadow: 1px 1px 80px var(--clr-blue2); }
100% { text-shadow: 1px 1px 30px var(--clr-blue2); }
}
.nav a::before {
content: "/";
color: var(--clr-purple2);
padding-right: 0.2em;
padding-left: 0.2em;
}
.nav a, .nav a:visited {
color: var(--clr-blue2);
text-decoration: none;
}
.nav a:hover {
color: var(--clr-green1);
text-shadow: 1px 1px 8px var(--clr-green1);
}
.nav a:active {
color: var(--clr-red1);
text-shadow: none;
}
.menu {
z-index: 3;
position: relative;
margin-inline: auto;
margin-top: 0.5em;
margin-bottom: 3em;
&::after {
z-index: -1;
position: absolute;
content: "";
top: 0;
left: 0;
height: 100%;
width: 100%;
background-color: rgba(86, 247, 201, 0.2);
animation: menu-blur 10s linear infinite;
}
}
@keyframes menu-blur {
0% { filter: blur(25px); }
30% { filter: blur(25px); }
50% { filter: blur(50px); }
60% { filter: blur(38px); }
100% { filter: blur(25px); }
}
.menu a, .menu a:visited {
--shadow-y: 1;
color: var(--clr-blue2);
text-decoration: none;
transition: text-shadow 0.2s ease-out;
text-shadow:
0px calc(8px * var(--shadow-y)) 1px hsl(160, 99%, 20%),
0px calc(16px * var(--shadow-y)) 1px hsl(320, 100%, 15%),
0px calc(24px * var(--shadow-y)) 1px hsl(345, 73%, 10%),
0px calc(32px * var(--shadow-y)) 1px hsl(360, 65%, 5%);
}
.menu a:hover {
--shadow-y: 0;
color: var(--clr-green1);
}
.menu a:active {
--shadow-y: 1;
color: var(--clr-purple2);
}
.menu a::before {
content: "[";
padding-right: 0.15em;
padding-left: 0.2em;
}
.menu a::after {
content: "]";
padding-right: 0.2em;
padding-left: 0.15em;
}
.no-ads {
z-index: 1;
display: flex;
align-items: center;
margin: 0;
width: 100%;
overflow: clip;
}
.no-ads-row {
display: flex;
width: max-content;
animation: scroll var(--no-ads-scroll-speed) linear infinite;
&:hover {
cursor: wait;
animation-play-state: paused;
}
}
.anim-stop {
animation-play-state: paused;
}
.no-ads-row > div {
width: 1280px;
height: 50px;
flex-shrink: 0;
& img {
width: 100%;
height: 100%;
}
}
@keyframes scroll {
to { transform: translateX(-50%); }
}
@media (max-width: 540px) {
.btn-anim-toggle {
visibility: hidden;
}
}
.btn-anim-toggle {
text-align: right;
padding: 1em 0.5em 0 0;
}
.btn-anim-toggle button {
color: var(--clr-green1);
padding: 0.3em;
border: 0;
background-color: transparent;
text-shadow: 0px 1px 6px var(--clr-green1);
&:visited {
color: var(--clr-green1);
}
&:hover {
color: var(--clr-purple1);
text-shadow: 0px 1px 6px var(--clr-purple2);
cursor: pointer;
}
&:active {
color: var(--clr-purple2);
}
}
.tunnel-fx {
z-index: 5;
display: flex;
flex-flow: row nowrap;
position: absolute;
width: inherit;
pointer-events: none;
}
.tunnel-fx-sides {
--tunnel-light: 0.35;
flex: 1;
border-radius: 15px;
animation: tunnel-opacity 26s linear infinite;
}
@keyframes tunnel-opacity {
0% { --tunnel-light: 0.35 }
38.9% { --tunnel-light: 0.35 }
39% { --tunnel-light: 0.1 }
39.2% { --tunnel-light: 0.5 }
39.4% { --tunnel-light: 0.1 }
39.6% { --tunnel-light: 0.5 }
39.7% { --tunnel-light: 0.1 }
39.8% { --tunnel-light: 0.5 }
40% { --tunnel-light: 0.35 }
50% { --tunnel-light: 0.35 }
70% { --tunnel-light: 0.35 }
70.1% { --tunnel-light: 0.1 }
70.2% { --tunnel-light: 0.5 }
70.4% { --tunnel-light: 0.1 }
70.6% { --tunnel-light: 0.5 }
70.8% { --tunnel-light: 0.1 }
71% { --tunnel-light: 0.35 }
100% { --tunnel-light: 0.35 }
}
.tunel-sides-r {
box-shadow: inset 24px 0 20px -20px rgba(14, 254, 174, var(--tunnel-light));
}
.tunel-sides-l {
box-shadow: inset -24px 0 20px -20px rgba(14, 254, 174, var(--tunnel-light));
}
.tunnel-fx-mid {
max-width: 1000px;
font-size: 2em;
padding: 0.8em;
margin: 0 auto;
text-shadow:
var(--title-offset) var(--title-offset) 1px hsl(175, 76%, 20%),
calc(var(--title-offset) * -1) calc(var(--title-offset) * -1) 1px hsl(320, 76%, 26%);
animation: rainbow 10s ease infinite;
background-color: var(--bg-color);
pointer-events: auto;
& span {
display: inline-block;
}
}
.t-title-1 {
animation: upDown1 var(--t-title-anim-duration) linear infinite;
}
.t-title-2 {
animation: upDown2 var(--t-title-anim-duration) linear infinite;
}
.t-title-3 {
animation: upDown3 var(--t-title-anim-duration) linear infinite;
}
.t-title-4 {
animation: upDown4 var(--t-title-anim-duration) linear infinite, flicker 30s linear infinite;
}
.t-title-5 {
animation: upDown5 var(--t-title-anim-duration) linear infinite;
}
.t-title-6 {
animation: upDown6 var(--t-title-anim-duration) linear infinite;
}
@keyframes upDown1 {
0% { transform: translateY(3px) rotate(-3deg); }
60% { transform: translateY(-3px) rotate(-3deg); }
80% { transform: translateY(1px) rotate(-3deg); }
100% { transform: translateY(3px) rotate(-3deg); }
}
@keyframes upDown2 {
0% { transform: translateY(-2px); }
60% { transform: translateY(0px); }
65% { transform: translateY(1px); }
80% { transform: translateY(0px); }
100% { transform: translateY(-2px); }
}
@keyframes upDown3 {
0% { transform: translateY(5px); }
40% { transform: translateY(6px); }
80% { transform: translateY(3px); }
100% { transform: translateY(5px); }
}
@keyframes upDown4 {
0% { transform: translateY(0px) rotate(-3deg); }
40% { transform: translateY(-2px) rotate(-3deg); }
80% { transform: translateY(-3px) rotate(-3deg); }
100% { transform: translateY(0px) rotate(-3deg); }
}
@keyframes upDown5 {
0% { transform: translateY(-4px); }
30% { transform: translateY(-10px); }
55% { transform: translateY(-8px); }
80% { transform: translateY(-2px); }
100% { transform: translateY(-4px); }
}
@keyframes upDown6 {
0% { transform: translateY(-2px) rotate(3deg); }
30% { transform: translateY(-2px) rotate(10deg); }
55% { transform: translateY(-2px) rotate(6deg); }
80% { transform: translateY(0px) rotate(2deg); }
100% { transform: translateY(-2px) rotate(3deg); }
}
@keyframes flicker {
0% { opacity: 1; }
7.8% { opacity: 1; }
7.9% { opacity: 0; }
8% { opacity: 1; }
8.3% { opacity: 0; }
8.9% { opacity: 1; }
9% { opacity: 0; }
11% { opacity: 1; }
20% { opacity: 1; }
30% { opacity: 1; }
40% { opacity: 1; }
50% { opacity: 1; }
59% { opacity: 1; }
59.1% { opacity: 0; }
59.2% { opacity: 0.5; }
59.3% { opacity: 0; }
59.5% { opacity: 0.5; }
59.9% { opacity: 0; }
60% { opacity: 0; }
69.7% { opacity: 0; }
69.8% { opacity: 0.5; }
69.9% { opacity: 0; }
71% { opacity: 1; }
80% { opacity: 1; }
90% { opacity: 1; }
100% { opacity: 1; }
}
.header {
border-top: dotted 1px var(--clr-green1);
border-bottom: dashed 1px var(--clr-green1);
padding-bottom: 0.12em;
margin-bottom: 0.3em;
box-shadow: 0px 5px 25px hsl(160, 99%, 22%);
}
.header-2 {
border-bottom: dotted 1px var(--clr-green1);
border-top: dashed 1px var(--clr-green1);
padding-bottom: 0.1em;
margin-bottom: 0.6em;
}
.header-spacer {
margin-top: 0.6em;
margin-bottom: 0;
}
@keyframes rainbow {
0% { color: hsl(120, 80%, 53%); }
25% { color: hsl(140, 89%, 53%); }
50% { color: hsl(160, 99%, 53%); }
75% { color: hsl(140, 89%, 53%); }
100% { color: hsl(120, 80%, 53%); }
}
.body {
display: flex;
flex-flow: column nowrap;
flex 1;
height: 100%;
min-height: 0;
width: 100%;
max-width: 1000px;
margin: 0 auto;
padding-top: 1em;
}
.body footer {
overflow-x: hidden;
position: relative;
text-align: center;
margin-top: 1em;
border-top: dashed 1px var(--clr-green1);
color: var(--clr-green2);
text-shadow: 0 0 6px var(--clr-green2);
& p::before {
position: absolute;
content: "•";
left: 1em;
}
& p::after {
position: absolute;
content: "•";
right: 1em;
}
}
.content {
flex: 1;
padding: 2em 1em 1em 1em;
min-height: 0;
scrollbar-color: var(--clr-blue2) var(--clr-green-dark);
scrollbar-width: thin;
scrollbar-gutter: stable;
overflow-y: auto;
overflow-x: hidden;
}
@media (max-width: 300px) {
.big-title {
line-break: anywhere;
}
}
.big-title {
color: var(--clr-blue2);
font-weight: bold;
font-style: italic;
text-transform: uppercase;
animation: big-title-shadow 9s linear infinite;
position: relative;
text-align: center;
font-size: 2em;
padding: 0.5em;
margin-bottom: 0.5em;
&::before {
content: "";
position: absolute;
inset: 0;
background: var(--clr-purple2);
/* Corners by https://css-generators.com/custom-corners */
clip-path: polygon(0 0,calc(100% - 15px) 0,100% 15px,100% 100%,15px 100%,0 calc(100% - 15px),0 0,1px 1px ,1px calc(100% - 15px - 0.41px),calc(15px + 0.41px) calc(100% - 1px),calc(100% - 1px) calc(100% - 1px),calc(100% - 1px) calc(15px + 0.41px),calc(100% - 15px - 0.41px) 1px,1px 1px);
}
}
@keyframes big-title-shadow {
0% { text-shadow: 0px 0px 25px var(--clr-blue1); }
68% { text-shadow: 0px 0px 50px var(--clr-blue1); }
100% { text-shadow: 0px 0px 25px var(--clr-blue1); }
}
.big-title-l {
bottom: -3px;
left: 0;
transform: rotate(-45deg);
position: absolute;
content: "";
width: 1px;
height: 10px;
background-color: var(--clr-green2);
box-shadow: 0 0 5px var(--clr-green2);
animation: bt-corners 1s ease ;
}
.big-title-r {
top: -3px;
right: 0;
transform: rotate(-45deg);
position: absolute;
content: "";
width: 1px;
height: 10px;
background-color: var(--clr-green2);
box-shadow: 0 0 5px var(--clr-green2);
animation: bt-corners 1s ease 1;
}
@keyframes bt-corners {
from { opacity: 0; }
to { opacity: 1;}
}
.simple-text {
padding: 1em 1em 2.5em 1em;
& a, &:visited {
text-decoration: underline;
color: var(--clr-green1);
}
& a:hover {
color: var(--clr-green2);
text-shadow: 0px 0px 7px var(--clr-green1);
}
& a:active {
color: var(--clr-green0);
text-shadow: none;
}
}
.st-center p {
text-align: center;
}
.st-green p {
color: var(--clr-green2);
}
.st-font-small p {
font-size: 1.1em;
}
.st-font-mid p {
font-size: 1.3em;
}
.st-font-bigger p {
font-size: 1.5em;
}
.st-font-big p {
font-size: 1.7em;
}
.collection {
color: var(--clr-green0);
& > div {
border: solid 1px var(--clr-blue1);
border-radius: 0 12px 0 12px;
padding: 1em;
margin-bottom: 1.4em;
box-shadow:
-1px -1px 0px var(--clr-green1),
1px 1px 0px var(--clr-purple2);
}
& div {
& div {
padding: 0 1.5em 0.5em 1.5em;
}
& a {
color: var(--clr-blue2);
text-decoration: none;
&:hover { text-shadow: 0 0 9px var(--clr-blue2); }
}
& a::after {
color: var(--clr-purple1);
opacity: 0.6;
margin-left: 0.5em;
cursor: default;
text-shadow: none;
}
}
& div > div { padding-top: 0.5em; }
& .l-tech a::after { content: " [tech]"; }
& .l-programming a::after { content: " [programming]"; }
& .l-art a::after { content: " [art]"; }
& .l-misc a::after { content: " [misc]"; }
}
.rnd-btn {
padding: 1em 1.5em 1em 1.5em;
color: var(--clr-green2);
font-size: 1em;
border: solid 1px var(--clr-green2);
border-radius: 0.7em;
border-inline-width: 7px;
box-shadow: inset 0 0 0 5px var(--clr-green0);
background-color: transparent;
text-shadow: 0 0 10px var(--clr-green0);
&:hover {
text-shadow: 0 0 10px var(--clr-green1);
box-shadow: inset 0 0 0 5px var(--clr-green-dark);
border-color: var(--clr-green3);
cursor: pointer;
}
&:active {
text-shadow: 0 0 7px var(--clr-green1);
border-color: var(--clr-green1);
transition: box-shadow 0.1s ease, transform 0.75s ease 0.2s;
box-shadow: inset 0 0 0 7px var(--clr-green-dark);
transform: rotateX(360deg);
}
}
.hide {
display: none;
}
</style>
</head>
<body>
<div class="main-wrapper">
<div class="screen-stripes"></div>
<div class="screen-grain"></div>
<div class="make-it-bigger"></div>
<div class="nav">
<ul>
<li><a href="#cat-all" title="All Links">all</a></li>
<li><a href="#cat-tech" title="Tech Links">tech</a></li>
<li><a href="#cat-programming" title="Programming Links">prog</a></li>
<li><a href="#cat-art" title="Art Links">art</a></li>
<li><a href="#cat-misc" title="Miscellaneous Links">misc</a></li>
</ul>
</div>
<div class="menu">
<ul>
<li><a href="#random" title="Random Links">Random</a></li>
<li><a href="#about" title="About">About</a></li>
<li><a href="#report" title="Report a link">Report Link</a></li>
<li><a href="https://codeberg.org/ptrace/link-collection" target="_blank" title="Source code">Source</a></li>
</ul>
</div>
<noscript>
<div style="padding-bottom: 1.5em; text-align: center; color: var(--clr-purple1);">
<p>Note: Javascript is not enabled. Since the rendering of the links relies on it.</p>
<p>But if you want to access the link collection without JS, you can use my API:
<a href="/links" target="_blank" title="API for all links">guksu.ptrace.dev/links</a></p>
</div>
</noscript>
<div class="no-ads">
<div class="tunnel-fx">
<div class="tunnel-fx-sides tunel-sides-l"></div>
<div class="tunnel-fx-mid">
<span class="t-title-1">welcome</span>
<span class="t-title-2">to</span>
<span class="t-title-3">my</span>
<span class="t-title-4">link</span>
<span class="t-title-5">colle</span><span class="t-title-6">ction!</span>
</div>
<div class="tunnel-fx-sides tunel-sides-r"></div>
</div>
<div class="no-ads-row anim-stop">
<!--
Supporting till 3440 x 1440
Anything beyond that is just madness.
The illusion of a infinite animation depends on following aspects:
- width of the containers below
- number of unique gif's
- chunk size (aka one group of unique gif's)
How many chunks you need is a little bit trial and error, which
also depends on the maximum window resolution you want to support.
In this case, there are 8x 160x50px images. Stiched together to one 1280x50px file.
There are to upsides: Less line noise and only a single request, instead of eight.
-->
<div><img src="img/no_ads.jpg"></div>
<div><img src="img/no_ads.jpg"></div>
<div><img src="img/no_ads.jpg"></div>
<div><img src="img/no_ads.jpg"></div>
<div><img src="img/no_ads.jpg"></div>
<div><img src="img/no_ads.jpg"></div>
<div><img src="img/no_ads.jpg"></div>
<div><img src="img/no_ads.jpg"></div>
</div>
</div>
<div class="btn-anim-toggle">
<button id="pause-anim-btn" onclick="toggle_banner_animation()"></button>
</div>
<div class="body">
<div class="header-spacer"></div>
<div class="header"></div>
<div class="header-2"></div>
<div class="content">
<!-- menu content -->
<div class="random">
<div class="big-title">
<span id="title-text">random</span>
<div class="big-title-l"></div>
<div class="big-title-r"></div>
</div>
<div class="simple-text st-center st-green st-font-mid">
<p>Down below are some random entries.</p>
<p>If you want a new set of links, just refresh this site
or press (F4) for a hotreload</p>
<p style="margin-top: 1.4em; font-size: 1.7em;">
<button class="rnd-btn" onclick="render_rnd_collection(); return false;">
I'm too lazy to press F4 or F5</button></p>
</div>
<div id="random-collection" class="collection"></div>
</div>
<div class="about">
<div class="big-title">
<span id="title-text">about</span>
<div class="big-title-l"></div>
<div class="big-title-r"></div>
</div>
<div class="simple-text st-green st-font-small">
<p>Back in the '90s and early 2000s, exploring the internet was a
different experience. Instead of algorithms, we relied on
recommendations from friends and curated link collections.</p>
<p>This project aims to revive that old spirit and contribute to
the culture that made the early internet so colorful.</p>
<p>The source code for this website is open and available
<a href="https://codeberg.org/ptrace/link-collection" target="_blank"
title="Source Code (Codeberg.org)">here</a>.</p>
<h2>Privacy</h2>
<p>This website does not collect any data, nor it serves any cookies.</p>
<h2>Disclaimer</h2>
<p>I bear no responsibility for the accuracy, legality or content of the
external site.</p>
<p>If an external site appears inappropriate, please
<a href="#report" title="Report a link">report</a> it to me.</p>
</div>
</div>
<div class="report">
<div class="big-title">
<span id="title-text">report link</span>
<div class="big-title-l"></div>
<div class="big-title-r"></div>
</div>
<div class="simple-text st-green st-font-small">
<p>I'm reviewing each link I publish, so nothing malignant slips through.</p>
<p>But if a site got hacked, or the web admin decides to go nuts,
I'll remove the link as soon as humanly possible.</p>
<p>If you encounter a malignant link, please report it to me via
e-mail:</p>
<p><a href="mailto:blog@ptrace.dev">blog@ptrace.dev</a>
(I'm also supporting GPG -> <a href="https://blog.ptrace.dev/key" target="_blank">Public Key</a>),
or create a <a href="https://codeberg.org/ptrace/link-collection/issues/new?title=Report+Link" target="_blank" title="creates a new ticket at my code repository">ticket</a> via my repository.</p>
<p style="font-size: 1.4em; margin: 2em 0 0 1em;">Thanks!</p>
</div>
</div>
<!-- category content -->
<div class="cat-all">
<div class="big-title">
<span id="title-text">all links</span>
<div class="big-title-l"></div>
<div class="big-title-r"></div>
</div>
<div id="all-links" class="collection cat-content"></div>
</div>
<div class="cat-tech">
<div class="big-title">
<span id="title-text">tech</span>
<div class="big-title-l"></div>
<div class="big-title-r"></div>
</div>
<div class="collection cat-content"></div>
</div>
<div class="cat-programming">
<div class="big-title">
<span id="title-text">programming</span>
<div class="big-title-l"></div>
<div class="big-title-r"></div>
</div>
<div class="collection cat-content"></div>
</div>
<div class="cat-art">
<div class="big-title">
<span id="title-text">art</span>
<div class="big-title-l"></div>
<div class="big-title-r"></div>
</div>
<div class="collection cat-content"></div>
</div>
<div class="cat-misc">
<div class="big-title">
<span id="title-text">misc</span>
<div class="big-title-l"></div>
<div class="big-title-r"></div>
</div>
<div class="collection cat-content"></div>
</div>
<!-- 404 -->
<div class="oohps hide">
<div class="big-title">
<span id="title-text">404 :/</span>
<div class="big-title-l"></div>
<div class="big-title-r"></div>
</div>
<div class="simple-text">
<p style="text-align: center; color: var(--clr-green2); font-size: 1.5em;">
I'm afraid, that I'm not able to serve the desired page to you
</p>
<p style="text-align: center; color: var(--clr-green2); font-size: 1.7em;">
sorry :(
</p>
<noscript>
<p style="text-align: center; color: var(--clr-green2); font-size: 1.1em;">
Note: This 404 message is always displayed if you have Javascript disabled
</p>
</noscript>
</div>
</div>
</div>
<footer>
<p>:: :: ::</p>
</footer>
</div>
</div>
<script type="text/javascript" src="./data.js"></script>
<script>
function asleep(ms) {
return new Promise(resolve => setTimeout(resolve, ms));
}
function asleep_dyn(str_len) {
const sleep_time = Math.floor(str_len * 0.05 * 1000);
return new Promise(resolve => setTimeout(resolve, sleep_time));
}
function enc(string) {
let buf = '';
for (let i = string.length - 1; i >= 0; i-- ) {
buf += String.fromCharCode(string.charCodeAt(i) + 1)
}
return buf;
}
async function dec(string, ref) {
let buf = '';
for (c of string) {
buf += String.fromCharCode(c.charCodeAt(0) - 1);
ref.innerText = buf;
await asleep(50);
}
buf = buf.split('');
let buf2 = buf.toReversed();
for (let i = buf.length - 1; i >= 0; i--) {
buf[i] = buf2[i];
let t = buf.join('');
ref.innerText = t;
await asleep(50);
}
}
function anim_title(hash) {
let title_elem = hash.replace('#', '.');
if (!title_elem) {
return;
}
let collection = document.querySelector(title_elem);
if (!collection) {
return;
}
let coll_children = collection.children;
let title = Array.from(coll_children).forEach((e => {
let elem = e.querySelector('#title-text');
if (!elem) return;
let _ = new Promise(resolve => {
const text = elem.innerText;
const encoded = enc(text);
dec(encoded, elem);
});
}))
}
let PAUSE_BANNER = true;
let LOCK_BIGGIE = false;
function toggle_banner_animation() {
const toggle = document.querySelector('#pause-anim-btn');
const no_ads_row = document.querySelector('.no-ads-row');
no_ads_row.classList.toggle('anim-stop');
if (PAUSE_BANNER) {
PAUSE_BANNER = false;
toggle.textContent = '[ PAUSE BANNER ]';
return;
}
PAUSE_BANNER = true;
toggle.textContent = '[ START BANNER ]';
}
function render_entry(name, cat, url, descr) {
const outer_div = document.createElement('div');
if (cat) {
outer_div.classList.add('l-' + cat);
}
const anchor = document.createElement('a');
anchor.href = url;
anchor.target = '_blank';
anchor.textContent = name;
const inner_div = document.createElement('div');
inner_div.textContent = descr;
outer_div.appendChild(anchor);
outer_div.appendChild(inner_div);
return outer_div;
}
function render_full_collection() {
const randEntries = data.entries;
randEntries.forEach(entry => {
const { name, category, url, description } = entry;
const outer_div = render_entry(name, category, url, description);
const container = document.querySelector('#all-links');
container.appendChild(outer_div);
});
}
function render_rnd_collection() {
const container = document.querySelector('#random-collection');
while (container.firstChild) {
container.removeChild(container.firstChild);
}
const randEntries = data.entries
.sort(() => 0.5 - Math.random())
.slice(0, 10);
randEntries.forEach(entry => {
const { name, category, url, description } = entry;
const outer_div = render_entry(name, category, url, description);
container.appendChild(outer_div);
});
}
function render_categories() {
data.entries.forEach(entry => {
const { name, category, url, description } = entry;
const outer_div = render_entry(name, null, url, description);
const container = document.querySelector('.cat-' + category).querySelector('.cat-content');
container.appendChild(outer_div);
})
}
async function terminal_print(string, ref) {
for (c of string) {
ref.textContent += c;
await asleep(50);
}
return string.length;
}
async function story_biggie(progress, ref) {
const lenny = [
'??',
'Yooo, stop clicking! (╯°□°)╯',
'Staaaahp ò_ô',
'Really? You want to push it even further? (ㆆ _ ㆆ)',
'Alright then...',
'carry on... (︶︹︶)',
'Great! Now you butchered this site ಠ_ಠ',
'and I have to programm a new one (ノಠ益ಠ)ノ彡┻━┻'
];
function clear() {
ref.replaceChildren();
}
function p() {
const p = document.createElement('p');
p.classList.add('blinky');
child = ref.appendChild(p);
return child;
}
async function scene(id) {
LOCK_BIGGIE = true;
const text_len = await terminal_print(lenny[id], p());
await asleep_dyn(text_len);
LOCK_BIGGIE = false;
}
clear();
switch (progress) {
case 10:
await scene(0);
break;
case 20:
await scene(1);
break;
case 40:
await scene(2);
break;
case 60:
await scene(3);
break;
case 70:
await scene(4);
clear();
await scene(5);
break;
case 100:
await scene(6);
clear();
await scene(7);
LOCK_BIGGIE = true;
break;
}
}
function make_it_bigger() {
const biggie = document.querySelector('.make-it-bigger');
if (!biggie) {
console.log('Biggie: Can not find div container');
return;
}
let first_click = false;
let w = 0;
let h = 0;
biggie.addEventListener('click', function() {
if (!first_click) {
first_click = true;
biggie.style.animation = 'none';
}
if (!LOCK_BIGGIE) {
w = w < 99 ? w + 10 : 100;
h = h < 99 ? h + 10 : 100;
biggie.style.width = w + 'vw';
biggie.style.height = h + 'vh';
story_biggie(w, biggie);
}
});
}
function content_reset() {
const other_divs = document.querySelectorAll('.content > div');
other_divs.forEach(section => section.classList.add('hide'));
}
function router(hash) {
let route = hash.replace('#', '');
content_reset();
if (!route) {
route = 'random'; // default page
}
const target_div = document.querySelector('.' + route);
if (!target_div) {
console.log('Error: Missing entry in content: ' + route);
document.querySelector('.oohps').classList.remove('hide');
return;
}
target_div.classList.remove('hide');
}
window.addEventListener("hashchange", function () {
const route = location.hash;
router(route);
anim_title(route);
})
document.addEventListener("keydown", function(event) {
if (event.key === 'F4') {
render_rnd_collection();
}
})
document.addEventListener("DOMContentLoaded", function () {
const route = window.location.hash;
render_rnd_collection();
render_categories();
render_full_collection();
router(route);
anim_title(route);
make_it_bigger();
toggle_banner_animation(); // Initial: animation off; in case someone has no js enabled
})
</script>
</body>
</html>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<link rel="icon" type="image/x-icon" href="img/guksu_logo.png">
<title>guksu - link collection</title>
<style>
:root {
--t-title-anim-duration: 5s;
--no-ads-scroll-speed: 180s;
--link-color: green;
--link-hover: red;
--link-active: yellow;
--bg-color: #030304;
/* 80s colors */
--clr-red1: hsl(349, 71%, 70%);
--clr-red2: hsl(345, 73%, 32%);
--clr-red3: hsl(345, 90%, 60%);
--clr-blue1: hsl(199, 58%, 53%);
--clr-blue2: hsl(191, 76%, 71%);
--clr-green-dark: hsl(160, 99%, 10%);
--clr-green0: hsl(160, 99%, 40%);
--clr-green1: hsl(160, 99%, 53%);
--clr-green2: hsl(160, 99%, 70%);
--clr-green3: hsl(160, 99%, 82%);
--clr-orange1: hsl(28, 68%, 42%);
--clr-yellow1: hsl(35, 79%, 65%);
--clr-purple1: hsl(317, 73%, 47%);
--clr-purple2: hsl(320, 100%, 52%);
/* pixels */
--title-offset: 2px;
}
body {
margin: 0;
background-color: var(--bg-color);
color: #e9e9fc;
color-scheme: dark;
font-family: Verdana, Geneva, Tahoma, sans-serif;
}
h2 {
color: var(--clr-blue2);
text-shadow: 0 0 12px var(--clr-blue1);
margin-top: 1.2em;
padding-bottom: 0.5em;
border-bottom: solid 1px var(--clr-blue2);
}
.main-wrapper {
display: flex;
flex-flow: column nowrap;
height: 100vh;
}
.screen-stripes {
z-index: 19;
position: absolute;
width: 100vw;
height: 100vh;
pointer-events: none;
&::before {
content: "";
background: repeating-linear-gradient(
var(--clr-green2),
var(--clr-green2) 1px,
transparent 1px,
transparent 3px
);
opacity: 0.04;
top: 0;
left: 0;
position: absolute;
width: 100%;
height: 100%;
}
}
.screen-grain {
z-index: 20;
position: absolute;
width: 100vw;
height: 100vh;
pointer-events: none;
&::before {
content: "";
background-color: hsl(191, 76%, 34%);
background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 600 600'%3E%3Cfilter id='a'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='.65' numOctaves='3' stitchTiles='stitch'/%3E%3C/filter%3E%3Crect width='100%25' height='100%25' filter='url(%23a)'/%3E%3C/svg%3E");
background-repeat: repeat;
background-size: 182px;
opacity: 0.07;
top: 0;
left: 0;
position: absolute;
width: 100%;
height: 100%;
}
}
.make-it-bigger {
display: flex;
flex-flow: column wrap;
z-index: 15;
position: absolute;
top: 0;
right: 0;
width: 2px;
height: 2px;
background-color: #000;
cursor: zoom-in;
user-select: none;
animation: mib-shine 15s linear infinite;
align-items: center;
justify-content: center;
font-size: 1.4em;
color: var(--clr-green2);
& p {
padding: 1em;
margin: 0.2em;
}
}
@keyframes mib-shine {
0% { background-color: transparent; }
98% { background-color: transparent; }
99% { background-color: var(--clr-green3); }
100% { background-color: transparent; }
}
.blinky::after {
content: "▌";
opacity: 1;
color: var(--clr-green2);
padding-left: 0.3em;
animation: blinky-blink 1.25s linear infinite;
}
@keyframes blinky-blink {
0% { opacity: 1; }
49% { opacity: 1; }
49.9% { opacity: 0; }
99.9% { opacity: 0; }
100% { opacity: 1; }
}
.nav {
display: flex;
flex-flow: row wrap;
font-size: 1.0rem;
padding: 0.5em;
}
.nav ul, .menu ul {
margin: 0;
list-style: none;
padding-left: 0;
}
.nav li, .menu li {
float: left;
list-style: none;
}
.nav::before {
color: var(--clr-green1);
content: "Categories:";
animation: nav-cat-shadow 9s linear infinite;
}
@keyframes nav-cat-shadow {
0% { text-shadow: 1px 1px 30px var(--clr-blue2); }
30% { text-shadow: 1px 1px 30px var(--clr-blue2); }
50% { text-shadow: 1px 1px 80px var(--clr-blue2); }
60% { text-shadow: 1px 1px 80px var(--clr-blue2); }
100% { text-shadow: 1px 1px 30px var(--clr-blue2); }
}
.nav a::before {
content: "/";
color: var(--clr-purple2);
padding-right: 0.2em;
padding-left: 0.2em;
}
.nav a, .nav a:visited {
color: var(--clr-blue2);
text-decoration: none;
}
.nav a:hover {
color: var(--clr-green1);
text-shadow: 1px 1px 8px var(--clr-green1);
}
.nav a:active {
color: var(--clr-red1);
text-shadow: none;
}
.menu {
z-index: 3;
position: relative;
margin-inline: auto;
margin-top: 0.5em;
margin-bottom: 3em;
&::after {
z-index: -1;
position: absolute;
content: "";
top: 0;
left: 0;
height: 100%;
width: 100%;
background-color: rgba(86, 247, 201, 0.2);
animation: menu-blur 10s linear infinite;
}
}
@keyframes menu-blur {
0% { filter: blur(25px); }
30% { filter: blur(25px); }
50% { filter: blur(50px); }
60% { filter: blur(38px); }
100% { filter: blur(25px); }
}
.menu a, .menu a:visited {
--shadow-y: 1;
color: var(--clr-blue2);
text-decoration: none;
transition: text-shadow 0.2s ease-out;
text-shadow:
0px calc(8px * var(--shadow-y)) 1px hsl(160, 99%, 20%),
0px calc(16px * var(--shadow-y)) 1px hsl(320, 100%, 15%),
0px calc(24px * var(--shadow-y)) 1px hsl(345, 73%, 10%),
0px calc(32px * var(--shadow-y)) 1px hsl(360, 65%, 5%);
}
.menu a:hover {
--shadow-y: 0;
color: var(--clr-green1);
}
.menu a:active {
--shadow-y: 1;
color: var(--clr-purple2);
}
.menu a::before {
content: "[";
padding-right: 0.15em;
padding-left: 0.2em;
}
.menu a::after {
content: "]";
padding-right: 0.2em;
padding-left: 0.15em;
}
.no-ads {
z-index: 1;
display: flex;
align-items: center;
margin: 0;
width: 100%;
overflow: clip;
}
.no-ads-row {
display: flex;
width: max-content;
animation: scroll var(--no-ads-scroll-speed) linear infinite;
&:hover {
cursor: wait;
animation-play-state: paused;
}
}
.anim-stop {
animation-play-state: paused;
}
.no-ads-row > div {
width: 1280px;
height: 50px;
flex-shrink: 0;
& img {
width: 100%;
height: 100%;
}
}
@keyframes scroll {
to { transform: translateX(-50%); }
}
@media (max-width: 540px) {
.btn-anim-toggle {
visibility: hidden;
}
}
.btn-anim-toggle {
text-align: right;
padding: 1em 0.5em 0 0;
}
.btn-anim-toggle button {
color: var(--clr-green1);
padding: 0.3em;
border: 0;
background-color: transparent;
text-shadow: 0px 1px 6px var(--clr-green1);
&:visited {
color: var(--clr-green1);
}
&:hover {
color: var(--clr-purple1);
text-shadow: 0px 1px 6px var(--clr-purple2);
cursor: pointer;
}
&:active {
color: var(--clr-purple2);
}
}
.tunnel-fx {
z-index: 5;
display: flex;
flex-flow: row nowrap;
position: absolute;
width: inherit;
pointer-events: none;
}
.tunnel-fx-sides {
--tunnel-light: 0.35;
flex: 1;
border-radius: 15px;
animation: tunnel-opacity 26s linear infinite;
}
@keyframes tunnel-opacity {
0% { --tunnel-light: 0.35 }
38.9% { --tunnel-light: 0.35 }
39% { --tunnel-light: 0.1 }
39.2% { --tunnel-light: 0.5 }
39.4% { --tunnel-light: 0.1 }
39.6% { --tunnel-light: 0.5 }
39.7% { --tunnel-light: 0.1 }
39.8% { --tunnel-light: 0.5 }
40% { --tunnel-light: 0.35 }
50% { --tunnel-light: 0.35 }
70% { --tunnel-light: 0.35 }
70.1% { --tunnel-light: 0.1 }
70.2% { --tunnel-light: 0.5 }
70.4% { --tunnel-light: 0.1 }
70.6% { --tunnel-light: 0.5 }
70.8% { --tunnel-light: 0.1 }
71% { --tunnel-light: 0.35 }
100% { --tunnel-light: 0.35 }
}
.tunel-sides-r {
box-shadow: inset 24px 0 20px -20px rgba(14, 254, 174, var(--tunnel-light));
}
.tunel-sides-l {
box-shadow: inset -24px 0 20px -20px rgba(14, 254, 174, var(--tunnel-light));
}
.tunnel-fx-mid {
max-width: 1000px;
font-size: 2em;
padding: 0.8em;
margin: 0 auto;
text-shadow:
var(--title-offset) var(--title-offset) 1px hsl(175, 76%, 20%),
calc(var(--title-offset) * -1) calc(var(--title-offset) * -1) 1px hsl(320, 76%, 26%);
animation: rainbow 10s ease infinite;
background-color: var(--bg-color);
pointer-events: auto;
& span {
display: inline-block;
}
}
.t-title-1 {
animation: upDown1 var(--t-title-anim-duration) linear infinite;
}
.t-title-2 {
animation: upDown2 var(--t-title-anim-duration) linear infinite;
}
.t-title-3 {
animation: upDown3 var(--t-title-anim-duration) linear infinite;
}
.t-title-4 {
animation: upDown4 var(--t-title-anim-duration) linear infinite, flicker 30s linear infinite;
}
.t-title-5 {
animation: upDown5 var(--t-title-anim-duration) linear infinite;
}
.t-title-6 {
animation: upDown6 var(--t-title-anim-duration) linear infinite;
}
@keyframes upDown1 {
0% { transform: translateY(3px) rotate(-3deg); }
60% { transform: translateY(-3px) rotate(-3deg); }
80% { transform: translateY(1px) rotate(-3deg); }
100% { transform: translateY(3px) rotate(-3deg); }
}
@keyframes upDown2 {
0% { transform: translateY(-2px); }
60% { transform: translateY(0px); }
65% { transform: translateY(1px); }
80% { transform: translateY(0px); }
100% { transform: translateY(-2px); }
}
@keyframes upDown3 {
0% { transform: translateY(5px); }
40% { transform: translateY(6px); }
80% { transform: translateY(3px); }
100% { transform: translateY(5px); }
}
@keyframes upDown4 {
0% { transform: translateY(0px) rotate(-3deg); }
40% { transform: translateY(-2px) rotate(-3deg); }
80% { transform: translateY(-3px) rotate(-3deg); }
100% { transform: translateY(0px) rotate(-3deg); }
}
@keyframes upDown5 {
0% { transform: translateY(-4px); }
30% { transform: translateY(-10px); }
55% { transform: translateY(-8px); }
80% { transform: translateY(-2px); }
100% { transform: translateY(-4px); }
}
@keyframes upDown6 {
0% { transform: translateY(-2px) rotate(3deg); }
30% { transform: translateY(-2px) rotate(10deg); }
55% { transform: translateY(-2px) rotate(6deg); }
80% { transform: translateY(0px) rotate(2deg); }
100% { transform: translateY(-2px) rotate(3deg); }
}
@keyframes flicker {
0% { opacity: 1; }
7.8% { opacity: 1; }
7.9% { opacity: 0; }
8% { opacity: 1; }
8.3% { opacity: 0; }
8.9% { opacity: 1; }
9% { opacity: 0; }
11% { opacity: 1; }
20% { opacity: 1; }
30% { opacity: 1; }
40% { opacity: 1; }
50% { opacity: 1; }
59% { opacity: 1; }
59.1% { opacity: 0; }
59.2% { opacity: 0.5; }
59.3% { opacity: 0; }
59.5% { opacity: 0.5; }
59.9% { opacity: 0; }
60% { opacity: 0; }
69.7% { opacity: 0; }
69.8% { opacity: 0.5; }
69.9% { opacity: 0; }
71% { opacity: 1; }
80% { opacity: 1; }
90% { opacity: 1; }
100% { opacity: 1; }
}
.header {
border-top: dotted 1px var(--clr-green1);
border-bottom: dashed 1px var(--clr-green1);
padding-bottom: 0.12em;
margin-bottom: 0.3em;
box-shadow: 0px 5px 25px hsl(160, 99%, 22%);
}
.header-2 {
border-bottom: dotted 1px var(--clr-green1);
border-top: dashed 1px var(--clr-green1);
padding-bottom: 0.1em;
margin-bottom: 0.6em;
}
.header-spacer {
margin-top: 0.6em;
margin-bottom: 0;
}
@keyframes rainbow {
0% { color: hsl(120, 80%, 53%); }
25% { color: hsl(140, 89%, 53%); }
50% { color: hsl(160, 99%, 53%); }
75% { color: hsl(140, 89%, 53%); }
100% { color: hsl(120, 80%, 53%); }
}
.body {
display: flex;
flex-flow: column nowrap;
flex 1;
height: 100%;
min-height: 0;
width: 100%;
max-width: 1000px;
margin: 0 auto;
padding-top: 1em;
}
.body footer {
overflow-x: hidden;
position: relative;
text-align: center;
margin-top: 1em;
border-top: dashed 1px var(--clr-green1);
color: var(--clr-green2);
text-shadow: 0 0 6px var(--clr-green2);
& p::before {
position: absolute;
content: "•";
left: 1em;
}
& p::after {
position: absolute;
content: "•";
right: 1em;
}
}
.content {
flex: 1;
padding: 2em 1em 1em 1em;
min-height: 0;
scrollbar-color: var(--clr-blue2) var(--clr-green-dark);
scrollbar-width: thin;
scrollbar-gutter: stable;
overflow-y: auto;
overflow-x: hidden;
}
@media (max-width: 300px) {
.big-title {
line-break: anywhere;
}
}
.big-title {
color: var(--clr-blue2);
font-weight: bold;
font-style: italic;
text-transform: uppercase;
animation: big-title-shadow 9s linear infinite;
position: relative;
text-align: center;
font-size: 2em;
padding: 0.5em;
margin-bottom: 0.5em;
&::before {
content: "";
position: absolute;
inset: 0;
background: var(--clr-purple2);
/* Corners by https://css-generators.com/custom-corners */
clip-path: polygon(0 0,calc(100% - 15px) 0,100% 15px,100% 100%,15px 100%,0 calc(100% - 15px),0 0,1px 1px ,1px calc(100% - 15px - 0.41px),calc(15px + 0.41px) calc(100% - 1px),calc(100% - 1px) calc(100% - 1px),calc(100% - 1px) calc(15px + 0.41px),calc(100% - 15px - 0.41px) 1px,1px 1px);
}
}
@keyframes big-title-shadow {
0% { text-shadow: 0px 0px 25px var(--clr-blue1); }
68% { text-shadow: 0px 0px 50px var(--clr-blue1); }
100% { text-shadow: 0px 0px 25px var(--clr-blue1); }
}
.big-title-l {
bottom: -3px;
left: 0;
transform: rotate(-45deg);
position: absolute;
content: "";
width: 1px;
height: 10px;
background-color: var(--clr-green2);
box-shadow: 0 0 5px var(--clr-green2);
animation: bt-corners 1s ease ;
}
.big-title-r {
top: -3px;
right: 0;
transform: rotate(-45deg);
position: absolute;
content: "";
width: 1px;
height: 10px;
background-color: var(--clr-green2);
box-shadow: 0 0 5px var(--clr-green2);
animation: bt-corners 1s ease 1;
}
@keyframes bt-corners {
from { opacity: 0; }
to { opacity: 1;}
}
.simple-text {
padding: 1em 1em 2.5em 1em;
& a, &:visited {
text-decoration: underline;
color: var(--clr-green1);
}
& a:hover {
color: var(--clr-green2);
text-shadow: 0px 0px 7px var(--clr-green1);
}
& a:active {
color: var(--clr-green0);
text-shadow: none;
}
}
.st-center p {
text-align: center;
}
.st-green p {
color: var(--clr-green2);
}
.st-font-small p {
font-size: 1.1em;
}
.st-font-mid p {
font-size: 1.3em;
}
.st-font-bigger p {
font-size: 1.5em;
}
.st-font-big p {
font-size: 1.7em;
}
.collection {
color: var(--clr-green0);
& > div {
border: solid 1px var(--clr-blue1);
border-radius: 0 12px 0 12px;
padding: 1em;
margin-bottom: 1.4em;
box-shadow:
-1px -1px 0px var(--clr-green1),
1px 1px 0px var(--clr-purple2);
}
& div {
& div {
padding: 0 1.5em 0.5em 1.5em;
}
& a {
color: var(--clr-blue2);
text-decoration: none;
&:hover { text-shadow: 0 0 9px var(--clr-blue2); }
}
& a::after {
color: var(--clr-purple1);
opacity: 0.6;
margin-left: 0.5em;
cursor: default;
text-shadow: none;
}
}
& div > div { padding-top: 0.5em; }
& .l-tech a::after { content: " [tech]"; }
& .l-programming a::after { content: " [programming]"; }
& .l-art a::after { content: " [art]"; }
& .l-misc a::after { content: " [misc]"; }
}
.rnd-btn {
padding: 1em 1.5em 1em 1.5em;
color: var(--clr-green2);
font-size: 1em;
border: solid 1px var(--clr-green2);
border-radius: 0.7em;
border-inline-width: 7px;
box-shadow: inset 0 0 0 5px var(--clr-green0);
background-color: transparent;
text-shadow: 0 0 10px var(--clr-green0);
&:hover {
text-shadow: 0 0 10px var(--clr-green1);
box-shadow: inset 0 0 0 5px var(--clr-green-dark);
border-color: var(--clr-green3);
cursor: pointer;
}
&:active {
text-shadow: 0 0 7px var(--clr-green1);
border-color: var(--clr-green1);
transition: box-shadow 0.1s ease, transform 0.75s ease 0.2s;
box-shadow: inset 0 0 0 7px var(--clr-green-dark);
transform: rotateX(360deg);
}
}
.hide {
display: none;
}
</style>
</head>
<body>
<div class="main-wrapper">
<div class="screen-stripes"></div>
<div class="screen-grain"></div>
<div class="make-it-bigger"></div>
<div class="nav">
<ul>
<li><a href="#cat-all" title="All Links">all</a></li>
<li><a href="#cat-tech" title="Tech Links">tech</a></li>
<li><a href="#cat-programming" title="Programming Links">prog</a></li>
<li><a href="#cat-art" title="Art Links">art</a></li>
<li><a href="#cat-misc" title="Miscellaneous Links">misc</a></li>
</ul>
</div>
<div class="menu">
<ul>
<li><a href="#random" title="Random Links">Random</a></li>
<li><a href="#about" title="About">About</a></li>
<li><a href="#report" title="Report a link">Report Link</a></li>
<li><a href="https://codeberg.org/ptrace/link-collection" target="_blank" title="Source code">Source</a></li>
</ul>
</div>
<noscript>
<div style="padding-bottom: 1.5em; text-align: center; color: var(--clr-purple1);">
<p>Note: Javascript is not enabled. Since the rendering of the links relies on it.</p>
<p>But if you want to access the link collection without JS, you can use my API:
<a href="/links" target="_blank" title="API for all links">guksu.ptrace.dev/links</a></p>
</div>
</noscript>
<div class="no-ads">
<div class="tunnel-fx">
<div class="tunnel-fx-sides tunel-sides-l"></div>
<div class="tunnel-fx-mid">
<span class="t-title-1">welcome</span>
<span class="t-title-2">to</span>
<span class="t-title-3">my</span>
<span class="t-title-4">link</span>
<span class="t-title-5">colle</span><span class="t-title-6">ction!</span>
</div>
<div class="tunnel-fx-sides tunel-sides-r"></div>
</div>
<div class="no-ads-row anim-stop">
<!--
Supporting till 3440 x 1440
Anything beyond that is just madness.
The illusion of a infinite animation depends on following aspects:
- width of the containers below
- number of unique gif's
- chunk size (aka one group of unique gif's)
How many chunks you need is a little bit trial and error, which
also depends on the maximum window resolution you want to support.
In this case, there are 8x 160x50px images. Stiched together to one 1280x50px file.
There are to upsides: Less line noise and only a single request, instead of eight.
-->
<div><img src="img/no_ads.jpg"></div>
<div><img src="img/no_ads.jpg"></div>
<div><img src="img/no_ads.jpg"></div>
<div><img src="img/no_ads.jpg"></div>
<div><img src="img/no_ads.jpg"></div>
<div><img src="img/no_ads.jpg"></div>
<div><img src="img/no_ads.jpg"></div>
<div><img src="img/no_ads.jpg"></div>
</div>
</div>
<div class="btn-anim-toggle">
<button id="pause-anim-btn" onclick="toggle_banner_animation()"></button>
</div>
<div class="body">
<div class="header-spacer"></div>
<div class="header"></div>
<div class="header-2"></div>
<div class="content">
<!-- menu content -->
<div class="random">
<div class="big-title">
<span id="title-text">random</span>
<div class="big-title-l"></div>
<div class="big-title-r"></div>
</div>
<div class="simple-text st-center st-green st-font-mid">
<p>Down below are some random entries.</p>
<p>If you want a new set of links, just refresh this site
or press (F4) for a hotreload</p>
<p style="margin-top: 1.4em; font-size: 1.7em;">
<button class="rnd-btn" onclick="render_rnd_collection(); return false;">
I'm too lazy to press F4 or F5</button></p>
</div>
<div id="random-collection" class="collection"></div>
</div>
<div class="about">
<div class="big-title">
<span id="title-text">about</span>
<div class="big-title-l"></div>
<div class="big-title-r"></div>
</div>
<div class="simple-text st-green st-font-small">
<p>Back in the '90s and early 2000s, exploring the internet was a
different experience. Instead of algorithms, we relied on
recommendations from friends and curated link collections.</p>
<p>This project aims to revive that old spirit and contribute to
the culture that made the early internet so colorful.</p>
<p>The source code for this website is open and available
<a href="https://codeberg.org/ptrace/link-collection" target="_blank"
title="Source Code (Codeberg.org)">here</a>.</p>
<h2>Privacy</h2>
<p>This website does not collect any data, nor it serves any cookies.</p>
<h2>Disclaimer</h2>
<p>I bear no responsibility for the accuracy, legality or content of the
external site.</p>
<p>If an external site appears inappropriate, please
<a href="#report" title="Report a link">report</a> it to me.</p>
</div>
</div>
<div class="report">
<div class="big-title">
<span id="title-text">report link</span>
<div class="big-title-l"></div>
<div class="big-title-r"></div>
</div>
<div class="simple-text st-green st-font-small">
<p>I'm reviewing each link I publish, so nothing malignant slips through.</p>
<p>But if a site got hacked, or the web admin decides to go nuts,
I'll remove the link as soon as humanly possible.</p>
<p>If you encounter a malignant link, please report it to me via
e-mail:</p>
<p><a href="mailto:blog@ptrace.dev">blog@ptrace.dev</a>
(I'm also supporting GPG -> <a href="https://blog.ptrace.dev/key" target="_blank">Public Key</a>),
or create a <a href="https://codeberg.org/ptrace/link-collection/issues/new?title=Report+Link" target="_blank" title="creates a new ticket at my code repository">ticket</a> via my repository.</p>
<p style="font-size: 1.4em; margin: 2em 0 0 1em;">Thanks!</p>
</div>
</div>
<!-- category content -->
<div class="cat-all">
<div class="big-title">
<span id="title-text">all links</span>
<div class="big-title-l"></div>
<div class="big-title-r"></div>
</div>
<div id="all-links" class="collection cat-content"></div>
</div>
<div class="cat-tech">
<div class="big-title">
<span id="title-text">tech</span>
<div class="big-title-l"></div>
<div class="big-title-r"></div>
</div>
<div class="collection cat-content"></div>
</div>
<div class="cat-programming">
<div class="big-title">
<span id="title-text">programming</span>
<div class="big-title-l"></div>
<div class="big-title-r"></div>
</div>
<div class="collection cat-content"></div>
</div>
<div class="cat-art">
<div class="big-title">
<span id="title-text">art</span>
<div class="big-title-l"></div>
<div class="big-title-r"></div>
</div>
<div class="collection cat-content"></div>
</div>
<div class="cat-misc">
<div class="big-title">
<span id="title-text">misc</span>
<div class="big-title-l"></div>
<div class="big-title-r"></div>
</div>
<div class="collection cat-content"></div>
</div>
<!-- 404 -->
<div class="oohps hide">
<div class="big-title">
<span id="title-text">404 :/</span>
<div class="big-title-l"></div>
<div class="big-title-r"></div>
</div>
<div class="simple-text">
<p style="text-align: center; color: var(--clr-green2); font-size: 1.5em;">
I'm afraid, that I'm not able to serve the desired page to you
</p>
<p style="text-align: center; color: var(--clr-green2); font-size: 1.7em;">
sorry :(
</p>
<noscript>
<p style="text-align: center; color: var(--clr-green2); font-size: 1.1em;">
Note: This 404 message is always displayed if you have Javascript disabled
</p>
</noscript>
</div>
</div>
</div>
<footer>
<p>:: :: ::</p>
</footer>
</div>
</div>
<script type="text/javascript" src="./data.js"></script>
<script>
function asleep(ms) {
return new Promise(resolve => setTimeout(resolve, ms));
}
function asleep_dyn(str_len) {
const sleep_time = Math.floor(str_len * 0.05 * 1000);
return new Promise(resolve => setTimeout(resolve, sleep_time));
}
function enc(string) {
let buf = '';
for (let i = string.length - 1; i >= 0; i-- ) {
buf += String.fromCharCode(string.charCodeAt(i) + 1)
}
return buf;
}
async function dec(string, ref) {
let buf = '';
for (c of string) {
buf += String.fromCharCode(c.charCodeAt(0) - 1);
ref.innerText = buf;
await asleep(50);
}
buf = buf.split('');
let buf2 = buf.toReversed();
for (let i = buf.length - 1; i >= 0; i--) {
buf[i] = buf2[i];
let t = buf.join('');
ref.innerText = t;
await asleep(50);
}
}
function anim_title(hash) {
let title_elem = hash.replace('#', '.');
if (!title_elem) {
return;
}
let collection = document.querySelector(title_elem);
if (!collection) {
return;
}
let coll_children = collection.children;
let title = Array.from(coll_children).forEach((e => {
let elem = e.querySelector('#title-text');
if (!elem) return;
let _ = new Promise(resolve => {
const text = elem.innerText;
const encoded = enc(text);
dec(encoded, elem);
});
}))
}
let PAUSE_BANNER = true;
let LOCK_BIGGIE = false;
function toggle_banner_animation() {
const toggle = document.querySelector('#pause-anim-btn');
const no_ads_row = document.querySelector('.no-ads-row');
no_ads_row.classList.toggle('anim-stop');
if (PAUSE_BANNER) {
PAUSE_BANNER = false;
toggle.textContent = '[ PAUSE BANNER ]';
return;
}
PAUSE_BANNER = true;
toggle.textContent = '[ START BANNER ]';
}
function render_entry(name, cat, url, descr) {
const outer_div = document.createElement('div');
if (cat) {
outer_div.classList.add('l-' + cat);
}
const anchor = document.createElement('a');
anchor.href = url;
anchor.target = '_blank';
anchor.textContent = name;
const inner_div = document.createElement('div');
inner_div.textContent = descr;
outer_div.appendChild(anchor);
outer_div.appendChild(inner_div);
return outer_div;
}
function render_full_collection() {
const randEntries = data.entries;
randEntries.forEach(entry => {
const { name, category, url, description } = entry;
const outer_div = render_entry(name, category, url, description);
const container = document.querySelector('#all-links');
container.appendChild(outer_div);
});
}
function render_rnd_collection() {
const container = document.querySelector('#random-collection');
while (container.firstChild) {
container.removeChild(container.firstChild);
}
const randEntries = data.entries
.sort(() => 0.5 - Math.random())
.slice(0, 10);
randEntries.forEach(entry => {
const { name, category, url, description } = entry;
const outer_div = render_entry(name, category, url, description);
container.appendChild(outer_div);
});
}
function render_categories() {
data.entries.forEach(entry => {
const { name, category, url, description } = entry;
const outer_div = render_entry(name, null, url, description);
const container = document.querySelector('.cat-' + category).querySelector('.cat-content');
container.appendChild(outer_div);
})
}
async function terminal_print(string, ref) {
for (c of string) {
ref.textContent += c;
await asleep(50);
}
return string.length;
}
async function story_biggie(progress, ref) {
const lenny = [
'??',
'Yooo, stop clicking! (╯°□°)╯',
'Staaaahp ò_ô',
'Really? You want to push it even further? (ㆆ _ ㆆ)',
'Alright then...',
'carry on... (︶︹︶)',
'Great! Now you butchered this site ಠ_ಠ',
'and I have to programm a new one (ノಠ益ಠ)ノ彡┻━┻'
];
function clear() {
ref.replaceChildren();
}
function p() {
const p = document.createElement('p');
p.classList.add('blinky');
child = ref.appendChild(p);
return child;
}
async function scene(id) {
LOCK_BIGGIE = true;
const text_len = await terminal_print(lenny[id], p());
await asleep_dyn(text_len);
LOCK_BIGGIE = false;
}
clear();
switch (progress) {
case 10:
await scene(0);
break;
case 20:
await scene(1);
break;
case 40:
await scene(2);
break;
case 60:
await scene(3);
break;
case 70:
await scene(4);
clear();
await scene(5);
break;
case 100:
await scene(6);
clear();
await scene(7);
LOCK_BIGGIE = true;
break;
}
}
function make_it_bigger() {
const biggie = document.querySelector('.make-it-bigger');
if (!biggie) {
console.log('Biggie: Can not find div container');
return;
}
let first_click = false;
let w = 0;
let h = 0;
biggie.addEventListener('click', function() {
if (!first_click) {
first_click = true;
biggie.style.animation = 'none';
}
if (!LOCK_BIGGIE) {
w = w < 99 ? w + 10 : 100;
h = h < 99 ? h + 10 : 100;
biggie.style.width = w + 'vw';
biggie.style.height = h + 'vh';
story_biggie(w, biggie);
}
});
}
function content_reset() {
const other_divs = document.querySelectorAll('.content > div');
other_divs.forEach(section => section.classList.add('hide'));
}
function router(hash) {
let route = hash.replace('#', '');
content_reset();
if (!route) {
route = 'random'; // default page
}
const target_div = document.querySelector('.' + route);
if (!target_div) {
console.log('Error: Missing entry in content: ' + route);
document.querySelector('.oohps').classList.remove('hide');
return;
}
target_div.classList.remove('hide');
}
window.addEventListener("hashchange", function () {
const route = location.hash;
router(route);
anim_title(route);
})
document.addEventListener("keydown", function(event) {
if (event.key === 'F4') {
render_rnd_collection();
}
})
document.addEventListener("DOMContentLoaded", function () {
const route = window.location.hash;
render_rnd_collection();
render_categories();
render_full_collection();
router(route);
anim_title(route);
make_it_bigger();
toggle_banner_animation(); // Initial: animation off; in case someone has no js enabled
})
</script>
</body>
</html>