···1-mixin contact(icon, title, href, button_text)
2- div.col-med-6.flex.flex-column.flex-justify-between
3- div
4- h3.text-center
5- i(class=`icon-${icon}`)
6- |
7- |
8- =title
9-10- p: block
11- div
12- +link(true, false)(href=href).btn.btn-primary
13- =button_text
14-15-div.row
16- div.col
17- h2 Contact
18- p You can find and contact me on many platforms. Choose the one you like below.
19-20- +contact("phone", "Phone", "tel:+4915678614220", "Call me")
21- | Give me a call at #[b +49 (0) 15678 614-220] for inquiries.
22- +contact("mail", "Email", "mailto:contact@scrumplex.net", "Write an email")
23- | Write me an email at #[b contact@scrumplex.net] for questions or help.
24- | PGP:
25- |
26- +link(true, true)(href="https://keys.openpgp.org/search?q=contact%40scrumplex.net")
27- code E13DFD4B47127951
28- +contact("matrix", "Matrix", "https://matrix.to/#/@scrumplex:duckhub.io", "Message me on Matrix")
29- | Contact me on Matrix via #[b @scrumplex:duckhub.io] and have a conversation with me.
30- +contact("telegram", "Telegram", "https://telegram.me/Scrumplex", "Message me on Telegram")
31- | Contact me on Telegram at #[b @Scrumplex] and have a conversation with me.
···1-p.text-justify.
2- Hello there,#[br]
3- I am Scrumplex, but you can call me Scrum, Scrump or Sefa.
4- I am an enthusiastic developer from <b>Germany</b>, and I invest lots of my time into playing around with many technologies.
5- Linux is my primary interest, as I maintain multiple servers and contribute to various open source technologies around the Linux space.
6- As for programming languages, I primarily use C/C++, Python, Bash, Go, Rust and Kotlin.
7- But I always try to incorporate other languages as well, if they fit the use-case.<br>
8- I know my way around CI/CD professionally, especially working with GitLab CI, Flux CD, Kubernetes and all the technologies around these.
9- And in an effort to apply my DevOps skills at home, I also do lots of #[+link(true, false)(href="https://nixos.org") Nix] stuff.
10- My daily computing is shaped by #[+link(true, false)(href="https://gnu.org/philosophy/free-sw.html") free software] and in an effort to ensure that it stays that way I generally publish all my work under a copyleft license.
11- If you want to see what I am working on right now, go to any of the code forges linked on this page.
···00000000000
-6
include/noscript.pug
···1-noscript
2- link(href="scss/noscript.scss", rel="stylesheet")
3- | Enable JavaScript for best experience. Source code of this website is available at
4- |
5- a(href="https://codeberg.org/Scrumplex/website", target="_blank", rel="noopener") Codeberg
6- | .
···000000
-46
include/projects.pug
···1-mixin project(title, logoImage, logoAlt, websiteHref, sourceHref)
2- div.col-smol-6.col-med-4.flex.flex-column.flex-justify-between
3- div
4- img.m-center(src=logoImage, alt=logoAlt, title=title, style="height: 70px", loading="lazy")
5- h3.text-center= title
6- p: block
7-8- div
9- if (websiteHref)
10- +link(true, false)(href=websiteHref).btn.btn-primary
11- | Website
12- if (sourceHref)
13- +link(true, false)(href=sourceHref).btn.btn-accent
14- | Source Code
15-16-17-div.row
18- div.col
19- h2 Current Projects
20-21-div.row
22- +project("Prism Launcher", "prismlauncher.svg", "Prism Launcher Logo", "https://prismlauncher.org", "https://github.com/PrismLauncher/PrismLauncher")
23- | A custom launcher for Minecraft that allows you to easily manage multiple installations of Minecraft at once.
24-25- +project("nixpkgs", "nixos.svg", "NixOS Logo", "https://nixos.org", "https://github.com/NixOS/nixpkgs")
26- | A reproducible toolchain for package management, system configuration and more.
27-28- +project("libvibrant", "vibrant.svg", "libvibrant Logo", null, "https://github.com/libvibrant")
29- | A collection of software to adjust color vibrancy and other color correction settings on Linux display servers.
30-31-div.row
32- div.col
33- h2 Legacy Projects
34-35-div.row
36- +project("PASSY", "passy.svg", "PASSY Logo", null, "https://gitlab.com/PASSYpw/PASSY")
37- | A beautiful password manager utilizing modern web technologies.
38-39- +project("Waves.js", "waves.js.svg", "Waves.js Logo", null, "https://gitlab.com/PASSYpw/Waves.js")
40- | A JQuery plugin providing authentic Material Design ripples.
41-42- +project("Sprummlbot", "sprummlbot.png", "Sprummlbot Logo", "https://scrumplex.rocks/cloud/sprummlbot-archive.tar.bz2", "https://gitlab.com/Scrumplex/Sprummlbot")
43- | A lightweight TeamSpeak 3 ServerAdmin Bot, adding many missing features to TeamSpeak 3 servers.
44-45- +project("ExitNow", "exitnow.png", "ExitNow Logo", "", "https://codeberg.org/Scrumplex/ExitNow")
46- | A simple application, providing an easy method to kill foreground windows with a shortcut.
···0000000000000000000000000000000000000000000000
+16-2
index.html
···5 <meta http-equiv="X-UA-Compatible" content="IE=edge" />
67 <title>Scrumplex · A passionate developer</title>
08 <link rel="stylesheet" href="scss/application.scss" />
910 <meta
···69 content="Hello there, my name is Sefa. Learn more on my website"
70 />
71 <meta name="page-topic" content="Scrumplex" />
72- <script type="module" src="js/application.js"></script>
73 </head>
74- <pug src="index.pug" />
0000000000000075</html>
···5 <meta http-equiv="X-UA-Compatible" content="IE=edge" />
67 <title>Scrumplex · A passionate developer</title>
8+ <link rel="stylesheet" href="global.css" />
9 <link rel="stylesheet" href="scss/application.scss" />
1011 <meta
···70 content="Hello there, my name is Sefa. Learn more on my website"
71 />
72 <meta name="page-topic" content="Scrumplex" />
073 </head>
74+ <body class="bg-primary scroll-smooth">
75+ <noscript>
76+ <link href="scss/noscript.scss" rel="stylesheet" />
77+ Enable JavaScript for best experience. Source code of this website
78+ is available at
79+ <a
80+ href="https://codeberg.org/Scrumplex/website"
81+ target="_blank"
82+ rel="noopener"
83+ >Codeberg</a
84+ >.
85+ </noscript>
86+ <div id="app"></div>
87+ <script prerender type="module" src="src/main.tsx"></script>
88+ </body>
89</html>
-58
index.pug
···1-include include/link
2-body.scroll
3- include include/noscript
4-5- div.container.wrapper#wrapper
6- div.sheet.sheet-splash.wavy#main
7- div.row
8- div.col-med-5.text-center.flex.flex-column.flex-justify-between
9- div.row
10- div.col
11- img.scrumplex-logo(src="scrumplex.svg", alt="Scrumplex Logo", title="Scrumplex", height=200, width=200)
12- h1 Scrumplex
13- strong he/him, they/them
14- div.row
15- div.col-med-6
16- h2 Development
17- +icon-link("codeberg", "Codeberg", "https://codeberg.org/Scrumplex")
18- +icon-link("github", "GitHub", "https://github.com/Scrumplex")
19- +icon-link("gitlab", "GitLab.com", "https://gitlab.com/Scrumplex")
20- div.col-med-6
21- h2 Donate
22- +icon-link("github_sponsors", "GitHub Sponsors", "https://github.com/sponsors/Scrumplex")
23- +icon-link("liberapay", "Liberapay", "https://liberapay.com/Scrumplex/donate")
24- +icon-link("paypal", "PayPal", "https://www.paypal.me/Scrumplex")
25- +icon-link("ko-fi", "Ko-Fi", "https://ko-fi.com/scrumplex")
26- div.col-med-7
27- blockquote.text-right
28- | Converting coffee to code...
29- |
30- span.text-spinner#text-spinner(hidden) |
31- include include/main
32-33- div.row
34- include include/footer
35-36- div.sheet.wavy#projects(hidden)
37- include include/projects
38-39- div.sheet.wavy#contact(hidden)
40- include include/contact
41-42- div.sheet.wavy#privacy(hidden)
43- div.row: div.col
44- h1 Privacy Policy
45- p.
46- Privacy information for services hosted on #[code scrumplex.net], #[code sefa.cloud] and #[code duckhub.io] (and all subdomains)
47-48- div.row: div.col: p.
49- Every service collects log-data. This includes IP addresses, browser user-agents and visited sites.
50- These are not used to track the user, but are only collected for operation purposes. #[br]
51- Some services may require you to enter some kind of username or email-address to access them.
52- These services can not operate without this information.
53- You may choose to use anonymous or pseudonymous usernames or email addresses.
54- All personal data (such as, but not limited to names, addresses or telephone numbers) shall always be in line with the General Data Protection Regulation. #[br]
55- No data is ever processed to track user activity.
56-57- div.scroll-indicator
58- h1.text-center ▾
···1-export default function ready() {
2- return new Promise((resolve) => {
3- if (document.readyState !== "loading") resolve();
4- else document.addEventListener("DOMContentLoaded", resolve);
5- });
6-}
···000000
-131
js/application.js
···1-/*!
2- * Personal website of Sefa Eyeoglu
3- * Copyright (C) 2018-2022 Sefa Eyeoglu <contact@scrumplex.net>
4- *
5- * This program is free software: you can redistribute it and/or modify
6- * it under the terms of the GNU Affero General Public License as published by
7- * the Free Software Foundation, either version 3 of the License, or
8- * (at your option) any later version.
9- *
10- * This program is distributed in the hope that it will be useful,
11- * but WITHOUT ANY WARRANTY; without even the implied warranty of
12- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13- * GNU Affero General Public License for more details.
14- *
15- * You should have received a copy of the GNU Affero General Public License
16- * along with this program. If not, see <https://www.gnu.org/licenses/>.
17- */
18-19-import ready from "./_utils";
20-21-const mainElem = document.getElementById("main");
22-23-document.querySelectorAll(".scrumplex-logo").forEach((elem) => {
24- elem.addEventListener("dblclick", () => {
25- const randomRotation = Math.floor(Math.random() * 360);
26- elem.style.transform = `rotate(${randomRotation}deg)`;
27- });
28-});
29-30-document.querySelectorAll("*[data-scroll]").forEach((elem) => {
31- elem.addEventListener("click", (e) => {
32- e.preventDefault();
33- const targetSelector = elem.getAttribute("data-scroll");
34- applyScrollSpecial(targetSelector);
35- });
36-});
37-38-mainElem.addEventListener("animationend", applyScrollInitially);
39-40-function applyScrollInitially() {
41- mainElem.removeEventListener("animationend", applyScrollInitially);
42-43- window.addEventListener("scroll", applyScrollConditionally);
44-45- if (location.hash.startsWith("#")) {
46- const result = applyScrollSpecial(location.hash);
47- if (result) return;
48- }
49- applyScrollConditionally();
50-}
51-52-function applyScrollConditionally() {
53- if (
54- mainElem.getBoundingClientRect().top <= 0 || // offset to top window border
55- mainElem.getBoundingClientRect().height >= window.innerHeight
56- )
57- applyScroll();
58-}
59-60-function applyScrollSpecial(targetSelector) {
61- if (!targetSelector) return false;
62- const targetElem = document.querySelector(targetSelector);
63- if (!targetElem) return false;
64- const offset = applyScroll(targetElem);
65- window.scrollTo({ top: offset, behavior: "smooth" });
66- history.pushState({ hash: targetSelector }, "", targetSelector);
67- return true;
68-}
69-70-function applyScroll(scrollTarget = null) {
71- // Black magic ahead!
72- // We sometimes need this method to show all sheets and then scroll to one of them.
73- // To achieve this we need to have a way to get the target position (like pixels) of
74- // the sheet we want to scroll to (in this case it's scrollTarget).
75- // As the bounding box always includes current transforms (so our translateY in the
76- // fade-in animation) we need to somehow get the boundingBox while our sheet is not
77- // being transformed. A hacky way to do this is to modify the behavior of .sheet[hidden]
78- // depending on the state of this method. In this case we are adding .scrolled to body
79- // which will cause all hidden sheets to instead be transparent.
80- // Additionally we will stop any animations that would be playing then.
81- // Now the element should be at it's target position so we just grab the position before
82- // un-hiding the elements and voila!
83- // One caveat: if the content changes after this method returned, the scroll might be a
84- // bit offset.
85-86- let offset = getElementScrollOffset(scrollTarget);
87- if (document.body.classList.contains("scrolled")) return offset;
88-89- const hiddenElems = document.querySelectorAll(".sheet[hidden]"),
90- posMain = getElementScrollOffset(mainElem); // offset to top rel. to document
91-92- document.getElementById("wrapper").style.paddingTop = `${posMain}px`;
93- document.body.classList.add("scrolled");
94-95- mainElem.classList.add("sheet-splashed");
96- mainElem.classList.remove("sheet-splash");
97- if (scrollTarget)
98- // recalculate, as it probably changed because of body.scrolled
99- offset = getElementScrollOffset(scrollTarget);
100-101- hiddenElems.forEach((elem) => {
102- elem.removeAttribute("hidden");
103- });
104-105- window.removeEventListener("scroll", applyScrollConditionally);
106- return offset;
107-}
108-109-function startSpinningChars(fps = 4) {
110- const spinningChars = ["|", "/", "-", "\\"],
111- spinnerElement = document.getElementById("text-spinner");
112-113- let currentIndex = 0;
114-115- setInterval(() => {
116- spinnerElement.innerText = spinningChars[currentIndex];
117- currentIndex++;
118- if (currentIndex >= spinningChars.length) currentIndex = 0;
119- }, 1000 / fps);
120-121- spinnerElement.removeAttribute("hidden");
122-}
123-124-function getElementScrollOffset(elem) {
125- if (elem) return window.pageYOffset + elem.getBoundingClientRect().top;
126- return -1;
127-}
128-129-ready().then(() => {
130- startSpinningChars();
131-});