···11-mixin contact(icon, title, href, button_text)
22- div.col-med-6.flex.flex-column.flex-justify-between
33- div
44- h3.text-center
55- i(class=`icon-${icon}`)
66- |
77- |
88- =title
99-1010- p: block
1111- div
1212- +link(true, false)(href=href).btn.btn-primary
1313- =button_text
1414-1515-div.row
1616- div.col
1717- h2 Contact
1818- p You can find and contact me on many platforms. Choose the one you like below.
1919-2020- +contact("phone", "Phone", "tel:+4915678614220", "Call me")
2121- | Give me a call at #[b +49 (0) 15678 614-220] for inquiries.
2222- +contact("mail", "Email", "mailto:contact@scrumplex.net", "Write an email")
2323- | Write me an email at #[b contact@scrumplex.net] for questions or help.
2424- | PGP:
2525- |
2626- +link(true, true)(href="https://keys.openpgp.org/search?q=contact%40scrumplex.net")
2727- code E13DFD4B47127951
2828- +contact("matrix", "Matrix", "https://matrix.to/#/@scrumplex:duckhub.io", "Message me on Matrix")
2929- | Contact me on Matrix via #[b @scrumplex:duckhub.io] and have a conversation with me.
3030- +contact("telegram", "Telegram", "https://telegram.me/Scrumplex", "Message me on Telegram")
3131- | Contact me on Telegram at #[b @Scrumplex] and have a conversation with me.
···11-p.text-justify.
22- Hello there,#[br]
33- I am Scrumplex, but you can call me Scrum, Scrump or Sefa.
44- I am an enthusiastic developer from <b>Germany</b>, and I invest lots of my time into playing around with many technologies.
55- Linux is my primary interest, as I maintain multiple servers and contribute to various open source technologies around the Linux space.
66- As for programming languages, I primarily use C/C++, Python, Bash, Go, Rust and Kotlin.
77- But I always try to incorporate other languages as well, if they fit the use-case.<br>
88- I know my way around CI/CD professionally, especially working with GitLab CI, Flux CD, Kubernetes and all the technologies around these.
99- 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.
1010- 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.
1111- If you want to see what I am working on right now, go to any of the code forges linked on this page.
-6
include/noscript.pug
···11-noscript
22- link(href="scss/noscript.scss", rel="stylesheet")
33- | Enable JavaScript for best experience. Source code of this website is available at
44- |
55- a(href="https://codeberg.org/Scrumplex/website", target="_blank", rel="noopener") Codeberg
66- | .
-46
include/projects.pug
···11-mixin project(title, logoImage, logoAlt, websiteHref, sourceHref)
22- div.col-smol-6.col-med-4.flex.flex-column.flex-justify-between
33- div
44- img.m-center(src=logoImage, alt=logoAlt, title=title, style="height: 70px", loading="lazy")
55- h3.text-center= title
66- p: block
77-88- div
99- if (websiteHref)
1010- +link(true, false)(href=websiteHref).btn.btn-primary
1111- | Website
1212- if (sourceHref)
1313- +link(true, false)(href=sourceHref).btn.btn-accent
1414- | Source Code
1515-1616-1717-div.row
1818- div.col
1919- h2 Current Projects
2020-2121-div.row
2222- +project("Prism Launcher", "prismlauncher.svg", "Prism Launcher Logo", "https://prismlauncher.org", "https://github.com/PrismLauncher/PrismLauncher")
2323- | A custom launcher for Minecraft that allows you to easily manage multiple installations of Minecraft at once.
2424-2525- +project("nixpkgs", "nixos.svg", "NixOS Logo", "https://nixos.org", "https://github.com/NixOS/nixpkgs")
2626- | A reproducible toolchain for package management, system configuration and more.
2727-2828- +project("libvibrant", "vibrant.svg", "libvibrant Logo", null, "https://github.com/libvibrant")
2929- | A collection of software to adjust color vibrancy and other color correction settings on Linux display servers.
3030-3131-div.row
3232- div.col
3333- h2 Legacy Projects
3434-3535-div.row
3636- +project("PASSY", "passy.svg", "PASSY Logo", null, "https://gitlab.com/PASSYpw/PASSY")
3737- | A beautiful password manager utilizing modern web technologies.
3838-3939- +project("Waves.js", "waves.js.svg", "Waves.js Logo", null, "https://gitlab.com/PASSYpw/Waves.js")
4040- | A JQuery plugin providing authentic Material Design ripples.
4141-4242- +project("Sprummlbot", "sprummlbot.png", "Sprummlbot Logo", "https://scrumplex.rocks/cloud/sprummlbot-archive.tar.bz2", "https://gitlab.com/Scrumplex/Sprummlbot")
4343- | A lightweight TeamSpeak 3 ServerAdmin Bot, adding many missing features to TeamSpeak 3 servers.
4444-4545- +project("ExitNow", "exitnow.png", "ExitNow Logo", "", "https://codeberg.org/Scrumplex/ExitNow")
4646- | A simple application, providing an easy method to kill foreground windows with a shortcut.
+16-2
index.html
···55 <meta http-equiv="X-UA-Compatible" content="IE=edge" />
6677 <title>Scrumplex · A passionate developer</title>
88+ <link rel="stylesheet" href="global.css" />
89 <link rel="stylesheet" href="scss/application.scss" />
9101011 <meta
···6970 content="Hello there, my name is Sefa. Learn more on my website"
7071 />
7172 <meta name="page-topic" content="Scrumplex" />
7272- <script type="module" src="js/application.js"></script>
7373 </head>
7474- <pug src="index.pug" />
7474+ <body class="bg-primary scroll-smooth">
7575+ <noscript>
7676+ <link href="scss/noscript.scss" rel="stylesheet" />
7777+ Enable JavaScript for best experience. Source code of this website
7878+ is available at
7979+ <a
8080+ href="https://codeberg.org/Scrumplex/website"
8181+ target="_blank"
8282+ rel="noopener"
8383+ >Codeberg</a
8484+ >.
8585+ </noscript>
8686+ <div id="app"></div>
8787+ <script prerender type="module" src="src/main.tsx"></script>
8888+ </body>
7589</html>
-58
index.pug
···11-include include/link
22-body.scroll
33- include include/noscript
44-55- div.container.wrapper#wrapper
66- div.sheet.sheet-splash.wavy#main
77- div.row
88- div.col-med-5.text-center.flex.flex-column.flex-justify-between
99- div.row
1010- div.col
1111- img.scrumplex-logo(src="scrumplex.svg", alt="Scrumplex Logo", title="Scrumplex", height=200, width=200)
1212- h1 Scrumplex
1313- strong he/him, they/them
1414- div.row
1515- div.col-med-6
1616- h2 Development
1717- +icon-link("codeberg", "Codeberg", "https://codeberg.org/Scrumplex")
1818- +icon-link("github", "GitHub", "https://github.com/Scrumplex")
1919- +icon-link("gitlab", "GitLab.com", "https://gitlab.com/Scrumplex")
2020- div.col-med-6
2121- h2 Donate
2222- +icon-link("github_sponsors", "GitHub Sponsors", "https://github.com/sponsors/Scrumplex")
2323- +icon-link("liberapay", "Liberapay", "https://liberapay.com/Scrumplex/donate")
2424- +icon-link("paypal", "PayPal", "https://www.paypal.me/Scrumplex")
2525- +icon-link("ko-fi", "Ko-Fi", "https://ko-fi.com/scrumplex")
2626- div.col-med-7
2727- blockquote.text-right
2828- | Converting coffee to code...
2929- |
3030- span.text-spinner#text-spinner(hidden) |
3131- include include/main
3232-3333- div.row
3434- include include/footer
3535-3636- div.sheet.wavy#projects(hidden)
3737- include include/projects
3838-3939- div.sheet.wavy#contact(hidden)
4040- include include/contact
4141-4242- div.sheet.wavy#privacy(hidden)
4343- div.row: div.col
4444- h1 Privacy Policy
4545- p.
4646- Privacy information for services hosted on #[code scrumplex.net], #[code sefa.cloud] and #[code duckhub.io] (and all subdomains)
4747-4848- div.row: div.col: p.
4949- Every service collects log-data. This includes IP addresses, browser user-agents and visited sites.
5050- These are not used to track the user, but are only collected for operation purposes. #[br]
5151- Some services may require you to enter some kind of username or email-address to access them.
5252- These services can not operate without this information.
5353- You may choose to use anonymous or pseudonymous usernames or email addresses.
5454- 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]
5555- No data is ever processed to track user activity.
5656-5757- div.scroll-indicator
5858- h1.text-center ▾
-6
js/_utils.js
···11-export default function ready() {
22- return new Promise((resolve) => {
33- if (document.readyState !== "loading") resolve();
44- else document.addEventListener("DOMContentLoaded", resolve);
55- });
66-}
-131
js/application.js
···11-/*!
22- * Personal website of Sefa Eyeoglu
33- * Copyright (C) 2018-2022 Sefa Eyeoglu <contact@scrumplex.net>
44- *
55- * This program is free software: you can redistribute it and/or modify
66- * it under the terms of the GNU Affero General Public License as published by
77- * the Free Software Foundation, either version 3 of the License, or
88- * (at your option) any later version.
99- *
1010- * This program is distributed in the hope that it will be useful,
1111- * but WITHOUT ANY WARRANTY; without even the implied warranty of
1212- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
1313- * GNU Affero General Public License for more details.
1414- *
1515- * You should have received a copy of the GNU Affero General Public License
1616- * along with this program. If not, see <https://www.gnu.org/licenses/>.
1717- */
1818-1919-import ready from "./_utils";
2020-2121-const mainElem = document.getElementById("main");
2222-2323-document.querySelectorAll(".scrumplex-logo").forEach((elem) => {
2424- elem.addEventListener("dblclick", () => {
2525- const randomRotation = Math.floor(Math.random() * 360);
2626- elem.style.transform = `rotate(${randomRotation}deg)`;
2727- });
2828-});
2929-3030-document.querySelectorAll("*[data-scroll]").forEach((elem) => {
3131- elem.addEventListener("click", (e) => {
3232- e.preventDefault();
3333- const targetSelector = elem.getAttribute("data-scroll");
3434- applyScrollSpecial(targetSelector);
3535- });
3636-});
3737-3838-mainElem.addEventListener("animationend", applyScrollInitially);
3939-4040-function applyScrollInitially() {
4141- mainElem.removeEventListener("animationend", applyScrollInitially);
4242-4343- window.addEventListener("scroll", applyScrollConditionally);
4444-4545- if (location.hash.startsWith("#")) {
4646- const result = applyScrollSpecial(location.hash);
4747- if (result) return;
4848- }
4949- applyScrollConditionally();
5050-}
5151-5252-function applyScrollConditionally() {
5353- if (
5454- mainElem.getBoundingClientRect().top <= 0 || // offset to top window border
5555- mainElem.getBoundingClientRect().height >= window.innerHeight
5656- )
5757- applyScroll();
5858-}
5959-6060-function applyScrollSpecial(targetSelector) {
6161- if (!targetSelector) return false;
6262- const targetElem = document.querySelector(targetSelector);
6363- if (!targetElem) return false;
6464- const offset = applyScroll(targetElem);
6565- window.scrollTo({ top: offset, behavior: "smooth" });
6666- history.pushState({ hash: targetSelector }, "", targetSelector);
6767- return true;
6868-}
6969-7070-function applyScroll(scrollTarget = null) {
7171- // Black magic ahead!
7272- // We sometimes need this method to show all sheets and then scroll to one of them.
7373- // To achieve this we need to have a way to get the target position (like pixels) of
7474- // the sheet we want to scroll to (in this case it's scrollTarget).
7575- // As the bounding box always includes current transforms (so our translateY in the
7676- // fade-in animation) we need to somehow get the boundingBox while our sheet is not
7777- // being transformed. A hacky way to do this is to modify the behavior of .sheet[hidden]
7878- // depending on the state of this method. In this case we are adding .scrolled to body
7979- // which will cause all hidden sheets to instead be transparent.
8080- // Additionally we will stop any animations that would be playing then.
8181- // Now the element should be at it's target position so we just grab the position before
8282- // un-hiding the elements and voila!
8383- // One caveat: if the content changes after this method returned, the scroll might be a
8484- // bit offset.
8585-8686- let offset = getElementScrollOffset(scrollTarget);
8787- if (document.body.classList.contains("scrolled")) return offset;
8888-8989- const hiddenElems = document.querySelectorAll(".sheet[hidden]"),
9090- posMain = getElementScrollOffset(mainElem); // offset to top rel. to document
9191-9292- document.getElementById("wrapper").style.paddingTop = `${posMain}px`;
9393- document.body.classList.add("scrolled");
9494-9595- mainElem.classList.add("sheet-splashed");
9696- mainElem.classList.remove("sheet-splash");
9797- if (scrollTarget)
9898- // recalculate, as it probably changed because of body.scrolled
9999- offset = getElementScrollOffset(scrollTarget);
100100-101101- hiddenElems.forEach((elem) => {
102102- elem.removeAttribute("hidden");
103103- });
104104-105105- window.removeEventListener("scroll", applyScrollConditionally);
106106- return offset;
107107-}
108108-109109-function startSpinningChars(fps = 4) {
110110- const spinningChars = ["|", "/", "-", "\\"],
111111- spinnerElement = document.getElementById("text-spinner");
112112-113113- let currentIndex = 0;
114114-115115- setInterval(() => {
116116- spinnerElement.innerText = spinningChars[currentIndex];
117117- currentIndex++;
118118- if (currentIndex >= spinningChars.length) currentIndex = 0;
119119- }, 1000 / fps);
120120-121121- spinnerElement.removeAttribute("hidden");
122122-}
123123-124124-function getElementScrollOffset(elem) {
125125- if (elem) return window.pageYOffset + elem.getBoundingClientRect().top;
126126- return -1;
127127-}
128128-129129-ready().then(() => {
130130- startSpinningChars();
131131-});