commit 9a8690ea68082eb0de15083f93e4f56e6b25229d
Author: Quinten0508 <55107945+Quinten0508@users.noreply.github.com>
Date: Sat Jun 22 20:58:35 2024 +0200
first commit
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..9b1ee42
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,175 @@
+# Based on https://raw.githubusercontent.com/github/gitignore/main/Node.gitignore
+
+# Logs
+
+logs
+_.log
+npm-debug.log_
+yarn-debug.log*
+yarn-error.log*
+lerna-debug.log*
+.pnpm-debug.log*
+
+# Caches
+
+.cache
+
+# Diagnostic reports (https://nodejs.org/api/report.html)
+
+report.[0-9]_.[0-9]_.[0-9]_.[0-9]_.json
+
+# Runtime data
+
+pids
+_.pid
+_.seed
+*.pid.lock
+
+# Directory for instrumented libs generated by jscoverage/JSCover
+
+lib-cov
+
+# Coverage directory used by tools like istanbul
+
+coverage
+*.lcov
+
+# nyc test coverage
+
+.nyc_output
+
+# Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files)
+
+.grunt
+
+# Bower dependency directory (https://bower.io/)
+
+bower_components
+
+# node-waf configuration
+
+.lock-wscript
+
+# Compiled binary addons (https://nodejs.org/api/addons.html)
+
+build/Release
+
+# Dependency directories
+
+node_modules/
+jspm_packages/
+
+# Snowpack dependency directory (https://snowpack.dev/)
+
+web_modules/
+
+# TypeScript cache
+
+*.tsbuildinfo
+
+# Optional npm cache directory
+
+.npm
+
+# Optional eslint cache
+
+.eslintcache
+
+# Optional stylelint cache
+
+.stylelintcache
+
+# Microbundle cache
+
+.rpt2_cache/
+.rts2_cache_cjs/
+.rts2_cache_es/
+.rts2_cache_umd/
+
+# Optional REPL history
+
+.node_repl_history
+
+# Output of 'npm pack'
+
+*.tgz
+
+# Yarn Integrity file
+
+.yarn-integrity
+
+# dotenv environment variable files
+
+.env
+.env.development.local
+.env.test.local
+.env.production.local
+.env.local
+
+# parcel-bundler cache (https://parceljs.org/)
+
+.parcel-cache
+
+# Next.js build output
+
+.next
+out
+
+# Nuxt.js build / generate output
+
+.nuxt
+dist
+
+# Gatsby files
+
+# Comment in the public line in if your project uses Gatsby and not Next.js
+
+# https://nextjs.org/blog/next-9-1#public-directory-support
+
+# public
+
+# vuepress build output
+
+.vuepress/dist
+
+# vuepress v2.x temp and cache directory
+
+.temp
+
+# Docusaurus cache and generated files
+
+.docusaurus
+
+# Serverless directories
+
+.serverless/
+
+# FuseBox cache
+
+.fusebox/
+
+# DynamoDB Local files
+
+.dynamodb/
+
+# TernJS port file
+
+.tern-port
+
+# Stores VSCode versions used for testing VSCode extensions
+
+.vscode-test
+
+# yarn v2
+
+.yarn/cache
+.yarn/unplugged
+.yarn/build-state.yml
+.yarn/install-state.gz
+.pnp.*
+
+# IntelliJ based IDEs
+.idea
+
+# Finder (MacOS) folder config
+.DS_Store
diff --git a/.unused/bg.png b/.unused/bg.png
new file mode 100644
index 0000000..972bc32
Binary files /dev/null and b/.unused/bg.png differ
diff --git a/.unused/dither copy.svg b/.unused/dither copy.svg
new file mode 100644
index 0000000..28f87cd
--- /dev/null
+++ b/.unused/dither copy.svg
@@ -0,0 +1,11 @@
+
\ No newline at end of file
diff --git a/.unused/dither.svg b/.unused/dither.svg
new file mode 100644
index 0000000..a25e5a4
--- /dev/null
+++ b/.unused/dither.svg
@@ -0,0 +1,11 @@
+
\ No newline at end of file
diff --git a/.unused/dithertest1.svg b/.unused/dithertest1.svg
new file mode 100644
index 0000000..5198327
--- /dev/null
+++ b/.unused/dithertest1.svg
@@ -0,0 +1,11 @@
+
\ No newline at end of file
diff --git a/.unused/index2.html b/.unused/index2.html
new file mode 100644
index 0000000..7ec8d3d
--- /dev/null
+++ b/.unused/index2.html
@@ -0,0 +1,23 @@
+
+
+
+
+
+SVG Background
+
+
+
+ hi :3
>
+
+
diff --git a/.unused/noise copy.svg b/.unused/noise copy.svg
new file mode 100644
index 0000000..39aece2
--- /dev/null
+++ b/.unused/noise copy.svg
@@ -0,0 +1,67 @@
+
\ No newline at end of file
diff --git a/.unused/noise.svg b/.unused/noise.svg
new file mode 100644
index 0000000..4b5e1ff
--- /dev/null
+++ b/.unused/noise.svg
@@ -0,0 +1,51 @@
+
\ No newline at end of file
diff --git a/404.html b/404.html
new file mode 100644
index 0000000..d6f2f6d
--- /dev/null
+++ b/404.html
@@ -0,0 +1,37 @@
+
+
+
+
+
+ quinten0508.com
+
+
+
+
+
+
+
+
+
+

+
+
+
+
+
+
+
+
+
+
+
diff --git a/assets/bg.gif b/assets/bg.gif
new file mode 100644
index 0000000..4b14c67
Binary files /dev/null and b/assets/bg.gif differ
diff --git a/assets/buttons/firefox.gif b/assets/buttons/firefox.gif
new file mode 100644
index 0000000..14a24df
Binary files /dev/null and b/assets/buttons/firefox.gif differ
diff --git a/assets/buttons/piracy.gif b/assets/buttons/piracy.gif
new file mode 100644
index 0000000..37b73b8
Binary files /dev/null and b/assets/buttons/piracy.gif differ
diff --git a/assets/buttons/yippeee.gif b/assets/buttons/yippeee.gif
new file mode 100644
index 0000000..c6a4035
Binary files /dev/null and b/assets/buttons/yippeee.gif differ
diff --git a/assets/capy.jpg b/assets/capy.jpg
new file mode 100644
index 0000000..b3d9973
Binary files /dev/null and b/assets/capy.jpg differ
diff --git a/assets/cat-hammer-404.gif b/assets/cat-hammer-404.gif
new file mode 100644
index 0000000..b1376e5
Binary files /dev/null and b/assets/cat-hammer-404.gif differ
diff --git a/assets/fonts/Nintendo-DS-BIOS.ttf b/assets/fonts/Nintendo-DS-BIOS.ttf
new file mode 100644
index 0000000..18e9603
Binary files /dev/null and b/assets/fonts/Nintendo-DS-BIOS.ttf differ
diff --git a/assets/fonts/ibm-plex-mono-v19-latin-500.woff2 b/assets/fonts/ibm-plex-mono-v19-latin-500.woff2
new file mode 100644
index 0000000..99c2610
Binary files /dev/null and b/assets/fonts/ibm-plex-mono-v19-latin-500.woff2 differ
diff --git a/assets/fonts/ibm-plex-mono-v19-latin-500italic.woff2 b/assets/fonts/ibm-plex-mono-v19-latin-500italic.woff2
new file mode 100644
index 0000000..f534ad6
Binary files /dev/null and b/assets/fonts/ibm-plex-mono-v19-latin-500italic.woff2 differ
diff --git a/assets/fonts/ibm-plex-mono-v19-latin-italic.woff2 b/assets/fonts/ibm-plex-mono-v19-latin-italic.woff2
new file mode 100644
index 0000000..0e72669
Binary files /dev/null and b/assets/fonts/ibm-plex-mono-v19-latin-italic.woff2 differ
diff --git a/assets/fonts/ibm-plex-mono-v19-latin-regular.woff2 b/assets/fonts/ibm-plex-mono-v19-latin-regular.woff2
new file mode 100644
index 0000000..a6c77d6
Binary files /dev/null and b/assets/fonts/ibm-plex-mono-v19-latin-regular.woff2 differ
diff --git a/assets/icons/lastfm.png b/assets/icons/lastfm.png
new file mode 100644
index 0000000..049e5b0
Binary files /dev/null and b/assets/icons/lastfm.png differ
diff --git a/assets/icons/lastfm_small.png b/assets/icons/lastfm_small.png
new file mode 100644
index 0000000..7905116
Binary files /dev/null and b/assets/icons/lastfm_small.png differ
diff --git a/assets/icons/moon_small.png b/assets/icons/moon_small.png
new file mode 100644
index 0000000..36776d7
Binary files /dev/null and b/assets/icons/moon_small.png differ
diff --git a/assets/icons/skull_small.png b/assets/icons/skull_small.png
new file mode 100644
index 0000000..bebb7e2
Binary files /dev/null and b/assets/icons/skull_small.png differ
diff --git a/assets/icons/sun_small.png b/assets/icons/sun_small.png
new file mode 100644
index 0000000..3db4051
Binary files /dev/null and b/assets/icons/sun_small.png differ
diff --git a/assets/icons/twitter.svg b/assets/icons/twitter.svg
new file mode 100644
index 0000000..d5acd46
--- /dev/null
+++ b/assets/icons/twitter.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/assets/image.png b/assets/image.png
new file mode 100644
index 0000000..8e85313
Binary files /dev/null and b/assets/image.png differ
diff --git a/assets/oneko-tux-old.gif b/assets/oneko-tux-old.gif
new file mode 100644
index 0000000..c4af325
Binary files /dev/null and b/assets/oneko-tux-old.gif differ
diff --git a/assets/oneko-tux.gif b/assets/oneko-tux.gif
new file mode 100644
index 0000000..b2dc823
Binary files /dev/null and b/assets/oneko-tux.gif differ
diff --git a/assets/oneko.gif b/assets/oneko.gif
new file mode 100644
index 0000000..a009c2c
Binary files /dev/null and b/assets/oneko.gif differ
diff --git a/assets/scripts/clock.js b/assets/scripts/clock.js
new file mode 100644
index 0000000..bee6fc3
--- /dev/null
+++ b/assets/scripts/clock.js
@@ -0,0 +1,27 @@
+function updateClock() {
+ const now = new Date().toLocaleString("en-US", {timeZone: "Europe/Amsterdam"});
+ const cestTime = new Date(now);
+
+ let hours = cestTime.getHours();
+ let minutes = cestTime.getMinutes();
+ let seconds = cestTime.getSeconds();
+
+ hours = hours < 10 ? '0' + hours : hours;
+ minutes = minutes < 10 ? '0' + minutes : minutes;
+ seconds = seconds < 10 ? '0' + seconds : seconds;
+
+
+ const timeString = hours + ':' + minutes + ':' + seconds;
+
+
+ //12pm //am
+ const isDaytime = hours >= 0 && hours > 7;
+ const imageElement = document.getElementById('clock-icon');
+ imageElement.src = isDaytime ? '/assets/icons/sun_small.png' : 'assets/icons/moon_small.png';
+ document.getElementById('clock').textContent = timeString;
+}
+
+ document.addEventListener("DOMContentLoaded", function(event) {
+ updateClock();
+ setInterval(updateClock, 1000);
+ });
\ No newline at end of file
diff --git a/assets/scripts/heartbeat.js b/assets/scripts/heartbeat.js
new file mode 100644
index 0000000..3e3803f
--- /dev/null
+++ b/assets/scripts/heartbeat.js
@@ -0,0 +1,42 @@
+document.addEventListener('DOMContentLoaded', async function() {
+ async function fetchLastBeat() {
+
+ const heartbeatElement = document.getElementById('heartbeat-time');
+
+ try {
+ const response = await fetch('https://quinten0508.com/api/heartbeat');
+ const data = await response.json();
+ const phoneDevice = data.find(device => device.device_name === 'Phone');
+ if (phoneDevice) {
+ const phoneTimestamp = phoneDevice.last_beat.timestamp;
+ const timestampMilliseconds = phoneTimestamp * 1000;
+
+ const currentTime = Date.now();
+ const difference = currentTime - timestampMilliseconds;
+
+ const isDead = difference > (1000 * 60 * 60 * 48);
+ const online = difference < (1000 * 60 * 5);
+ if (online) {
+ heartbeatElement.textContent = 'Online!';
+ heartbeatElement.style = 'color: #00b400';
+ return;
+ } else if (isDead) {
+ heartbeatElement.textContent = 'Dead';
+ heartbeatElement.style = 'color: #b30000';
+ } else {
+ heartbeatElement.textContent = 'Alive';
+ heartbeatElement.style = 'color: #00b400';
+ }
+
+ } else {
+ console.error('Device "Phone" not found.');
+ }
+ } catch (error) {
+ console.error('Error fetching last beat:', error);
+ }
+ }
+
+ // Fetch the last beat immediately and then every 10 seconds
+ fetchLastBeat();
+ setInterval(fetchLastBeat, 10000);
+});
diff --git a/assets/scripts/lastfm.js b/assets/scripts/lastfm.js
new file mode 100644
index 0000000..8de4d19
--- /dev/null
+++ b/assets/scripts/lastfm.js
@@ -0,0 +1,81 @@
+document.addEventListener('DOMContentLoaded', () => {
+ const nowPlayingElement = document.getElementById('lastfm-contents');
+ const lastfmArtElement = document.getElementById('lastfm-artbox');
+
+ function fetchNowPlaying() {
+ fetch('https://quinten0508.com/api/nowplaying')
+ .then(response => response.json())
+ .then(data => {
+ if (data.recenttracks.track && data.recenttracks.track.length > 0) {
+ const tracks = data.recenttracks.track;
+ const nowPlaying = tracks[0];
+ const lastPlayedSong = tracks[1];
+ const lastlastPlayedSong = tracks[2];
+
+ if (nowPlaying['@attr'] && nowPlaying['@attr'].nowplaying) {
+ const nowPlayingHtml = `
+ Now Playing:
+ 1. ${nowPlaying.artist['#text']} - ${nowPlaying.name}
+
+ 2. ${lastPlayedSong.artist['#text']} - ${lastPlayedSong.name}
+
+
+ 3. ${lastlastPlayedSong.artist['#text']} - ${lastlastPlayedSong.name}
+
+ `;
+ nowPlayingElement.innerHTML = nowPlayingHtml;
+ const albumArt = nowPlaying.image.find(img => img.size === 'large')['#text'];
+ lastfmArtElement.innerHTML = `
`;
+ } else {
+ const lastPlayed = tracks[0];
+ const lastPlayedTime = new Date(lastPlayed.date.uts * 1000);
+ const currentTime = new Date();
+ const timeDiff = Math.floor((currentTime - lastPlayedTime) / 1000);
+ const timeSince = formatTimeSince(timeDiff);
+
+ const lastPlayedHtml = `
+ Last Played:
+ ${lastPlayed.artist['#text']} - ${lastPlayed.name}
+ ${timeSince} ago
+ `;
+ nowPlayingElement.innerHTML = lastPlayedHtml;
+ const albumArt = lastPlayed.image.find(img => img.size === 'large')['#text'];
+ lastfmArtElement.innerHTML = `
`;
+ }
+ } else {
+ nowPlayingElement.innerHTML = `Now Playing:
Nothing`;
+ lastfmArtElement.innerHTML = '';
+ }
+ })
+ .catch(error => {
+ nowPlayingElement.textContent = 'Error fetching last.fm status.';
+ lastfmArtElement.innerHTML = '';
+ console.error('Error fetching now playing status:', error);
+ });
+ }
+
+
+
+ function formatTimeSince(seconds) {
+ const intervals = [
+ { label: 'year', seconds: 31536000 },
+ { label: 'month', seconds: 2592000 },
+ { label: 'day', seconds: 86400 },
+ { label: 'hour', seconds: 3600 },
+ { label: 'minute', seconds: 60 },
+ { label: 'second', seconds: 1 }
+ ];
+
+ for (const interval of intervals) {
+ const count = Math.floor(seconds / interval.seconds);
+ if (count > 0) {
+ return `${count} ${interval.label}${count !== 1 ? 's' : ''}`;
+ }
+ }
+ return 'just now';
+ }
+
+ fetchNowPlaying();
+
+ setInterval(fetchNowPlaying, 10000);
+});
diff --git a/assets/scripts/lastfm.js.bak b/assets/scripts/lastfm.js.bak
new file mode 100644
index 0000000..1b437af
--- /dev/null
+++ b/assets/scripts/lastfm.js.bak
@@ -0,0 +1,93 @@
+document.addEventListener('DOMContentLoaded', () => {
+ const apiKey = 'APIKEY CHANGE ME'; //these are free, why would you...
+ const username = 'Quinten0508';
+
+ const nowPlayingElement = document.getElementById('lastfm-contents');
+ const lastfmArtElement = document.getElementById('lastfm-artbox');
+ const topTrackElement = document.getElementById('lastfm-toptrack');
+
+ function fetchNowPlaying() {
+ fetch(`https://ws.audioscrobbler.com/2.0/?method=user.getrecenttracks&user=${username}&api_key=${apiKey}&format=json`)
+ .then(response => response.json())
+ .then(data => {
+ if (data.recenttracks.track && data.recenttracks.track.length > 0) {
+ const tracks = data.recenttracks.track;
+ const nowPlaying = tracks[0];
+ const lastPlayedSong = tracks[1];
+ const lastlastPlayedSong = tracks[2];
+
+ if (nowPlaying['@attr'] && nowPlaying['@attr'].nowplaying) { //absolute clusterfuck incoming SORRY - rewrite and send this mess to the css file instead!
+ nowPlayingElement.innerHTML = `Now Playing:
1. ${nowPlaying.artist['#text']} - ${nowPlaying.name} 2. ${lastPlayedSong.artist['#text']} - ${lastPlayedSong.name}
3. ${lastlastPlayedSong.artist['#text']} - ${lastlastPlayedSong.name}
`;
+ const albumArt = nowPlaying.image.find(img => img.size === 'large')['#text'];
+ lastfmArtElement.innerHTML = `
`;
+ } else {
+ const lastPlayed = tracks[0];
+ const lastPlayedTime = new Date(lastPlayed.date.uts * 1000);
+ const currentTime = new Date();
+ const timeDiff = Math.floor((currentTime - lastPlayedTime) / 1000);
+ const timeSince = formatTimeSince(timeDiff);
+
+ nowPlayingElement.innerHTML = `Last Played:
${lastPlayed.artist['#text']} - ${lastPlayed.name}
${timeSince} ago`;
+ const albumArt = lastPlayed.image.find(img => img.size === 'large')['#text'];
+ lastfmArtElement.innerHTML = `
`;
+ }
+ } else {
+ nowPlayingElement.innerHTML = `Now Playing:
Nothing`;
+ lastfmArtElement.innerHTML = '';
+ }
+ })
+ .catch(error => {
+ nowPlayingElement.textContent = 'Error fetching last.fm status.';
+ lastfmArtElement.innerHTML = '';
+ console.error('Error fetching now playing status:', error);
+ });
+ }
+
+ function formatTimeSince(seconds) {
+ const intervals = [
+ { label: 'year', seconds: 31536000 },
+ { label: 'month', seconds: 2592000 },
+ { label: 'day', seconds: 86400 },
+ { label: 'hour', seconds: 3600 },
+ { label: 'minute', seconds: 60 },
+ { label: 'second', seconds: 1 }
+ ];
+
+ for (const interval of intervals) {
+ const count = Math.floor(seconds / interval.seconds);
+ if (count > 0) {
+ return `${count} ${interval.label}${count !== 1 ? 's' : ''}`;
+ }
+ }
+ return 'just now';
+ }
+
+ fetchNowPlaying();
+
+ setInterval(fetchNowPlaying, 10000);
+});
+
+/*
+
+ // fetchTopTrack();
+ function fetchTopTrack() {
+ fetch(`https://ws.audioscrobbler.com/2.0/?method=user.gettoptracks&user=${username}&api_key=${apiKey}&format=json&period=1month&limit=1`)
+ .then(response => response.json())
+ .then(data => {
+ if (data.toptracks.track && data.toptracks.track.length > 0) {
+ const topTrack = data.toptracks.track[0];
+ const topTrackName = topTrack.name;
+ const topTrackArtist = topTrack.artist.name;
+ const topTrackPlayCount = topTrack.playcount;
+ topTrackElement.innerHTML = `Top Track This Month:
${topTrackArtist} - ${topTrackName}
Play Count: ${topTrackPlayCount}`;
+ nowPlayingElement.innerHTML(topTrackElement);
+ } else {
+ console.error('No top track found.');
+ }
+ })
+ .catch(error => {
+ console.error('Error fetching top track:', error);
+ });
+ }
+
+ */
\ No newline at end of file
diff --git a/assets/scripts/oneko.js b/assets/scripts/oneko.js
new file mode 100644
index 0000000..bd8b882
--- /dev/null
+++ b/assets/scripts/oneko.js
@@ -0,0 +1,239 @@
+// oneko.js: https://github.com/adryd325/oneko.js
+
+(function oneko() {
+ const isReducedMotion =
+ window.matchMedia(`(prefers-reduced-motion: reduce)`) === true ||
+ window.matchMedia(`(prefers-reduced-motion: reduce)`).matches === true;
+
+ if (isReducedMotion) return;
+
+ const nekoEl = document.createElement("div");
+
+ let nekoPosX = 32;
+ let nekoPosY = 32;
+
+ let mousePosX = 0;
+ let mousePosY = 0;
+
+ let frameCount = 0;
+ let idleTime = 0;
+ let idleAnimation = null;
+ let idleAnimationFrame = 0;
+
+ const nekoSpeed = 10;
+ const spriteSets = {
+ idle: [[-3, -3]],
+ alert: [[-7, -3]],
+ scratchSelf: [
+ [-5, 0],
+ [-6, 0],
+ [-7, 0],
+ ],
+ scratchWallN: [
+ [0, 0],
+ [0, -1],
+ ],
+ scratchWallS: [
+ [-7, -1],
+ [-6, -2],
+ ],
+ scratchWallE: [
+ [-2, -2],
+ [-2, -3],
+ ],
+ scratchWallW: [
+ [-4, 0],
+ [-4, -1],
+ ],
+ tired: [[-3, -2]],
+ sleeping: [
+ [-2, 0],
+ [-2, -1],
+ ],
+ N: [
+ [-1, -2],
+ [-1, -3],
+ ],
+ NE: [
+ [0, -2],
+ [0, -3],
+ ],
+ E: [
+ [-3, 0],
+ [-3, -1],
+ ],
+ SE: [
+ [-5, -1],
+ [-5, -2],
+ ],
+ S: [
+ [-6, -3],
+ [-7, -2],
+ ],
+ SW: [
+ [-5, -3],
+ [-6, -1],
+ ],
+ W: [
+ [-4, -2],
+ [-4, -3],
+ ],
+ NW: [
+ [-1, 0],
+ [-1, -1],
+ ],
+ };
+
+ function init() {
+ nekoEl.id = "oneko";
+ nekoEl.ariaHidden = true;
+ nekoEl.style.width = "32px";
+ nekoEl.style.height = "32px";
+ nekoEl.style.position = "fixed";
+ nekoEl.style.pointerEvents = "none";
+ nekoEl.style.imageRendering = "pixelated";
+ nekoEl.style.left = `${nekoPosX - 16}px`;
+ nekoEl.style.top = `${nekoPosY - 16}px`;
+ nekoEl.style.zIndex = Number.MAX_VALUE;
+
+ let nekoFile = "./oneko.gif"
+ const curScript = document.currentScript
+ if (curScript && curScript.dataset.cat) {
+ nekoFile = curScript.dataset.cat
+ }
+ nekoEl.style.backgroundImage = `url(${nekoFile})`;
+
+ document.body.appendChild(nekoEl);
+
+ document.addEventListener("mousemove", function (event) {
+ mousePosX = event.clientX;
+ mousePosY = event.clientY;
+ });
+
+ window.requestAnimationFrame(onAnimationFrame);
+ }
+
+ let lastFrameTimestamp;
+
+ function onAnimationFrame(timestamp) {
+ // Stops execution if the neko element is removed from DOM
+ if (!nekoEl.isConnected) {
+ return;
+ }
+ if (!lastFrameTimestamp) {
+ lastFrameTimestamp = timestamp;
+ }
+ if (timestamp - lastFrameTimestamp > 100) {
+ lastFrameTimestamp = timestamp
+ frame()
+ }
+ window.requestAnimationFrame(onAnimationFrame);
+ }
+
+ function setSprite(name, frame) {
+ const sprite = spriteSets[name][frame % spriteSets[name].length];
+ nekoEl.style.backgroundPosition = `${sprite[0] * 32}px ${sprite[1] * 32}px`;
+ }
+
+ function resetIdleAnimation() {
+ idleAnimation = null;
+ idleAnimationFrame = 0;
+ }
+
+ function idle() {
+ idleTime += 1;
+
+ // every ~ 20 seconds
+ if (
+ idleTime > 10 &&
+ Math.floor(Math.random() * 200) == 0 &&
+ idleAnimation == null
+ ) {
+ let avalibleIdleAnimations = ["sleeping", "scratchSelf"];
+ if (nekoPosX < 32) {
+ avalibleIdleAnimations.push("scratchWallW");
+ }
+ if (nekoPosY < 32) {
+ avalibleIdleAnimations.push("scratchWallN");
+ }
+ if (nekoPosX > window.innerWidth - 32) {
+ avalibleIdleAnimations.push("scratchWallE");
+ }
+ if (nekoPosY > window.innerHeight - 32) {
+ avalibleIdleAnimations.push("scratchWallS");
+ }
+ idleAnimation =
+ avalibleIdleAnimations[
+ Math.floor(Math.random() * avalibleIdleAnimations.length)
+ ];
+ }
+
+ switch (idleAnimation) {
+ case "sleeping":
+ if (idleAnimationFrame < 8) {
+ setSprite("tired", 0);
+ break;
+ }
+ setSprite("sleeping", Math.floor(idleAnimationFrame / 4));
+ if (idleAnimationFrame > 192) {
+ resetIdleAnimation();
+ }
+ break;
+ case "scratchWallN":
+ case "scratchWallS":
+ case "scratchWallE":
+ case "scratchWallW":
+ case "scratchSelf":
+ setSprite(idleAnimation, idleAnimationFrame);
+ if (idleAnimationFrame > 9) {
+ resetIdleAnimation();
+ }
+ break;
+ default:
+ setSprite("idle", 0);
+ return;
+ }
+ idleAnimationFrame += 1;
+ }
+
+ function frame() {
+ frameCount += 1;
+ const diffX = nekoPosX - mousePosX;
+ const diffY = nekoPosY - mousePosY;
+ const distance = Math.sqrt(diffX ** 2 + diffY ** 2);
+
+ if (distance < nekoSpeed || distance < 48) {
+ idle();
+ return;
+ }
+
+ idleAnimation = null;
+ idleAnimationFrame = 0;
+
+ if (idleTime > 1) {
+ setSprite("alert", 0);
+ // count down after being alerted before moving
+ idleTime = Math.min(idleTime, 7);
+ idleTime -= 1;
+ return;
+ }
+
+ let direction;
+ direction = diffY / distance > 0.5 ? "N" : "";
+ direction += diffY / distance < -0.5 ? "S" : "";
+ direction += diffX / distance > 0.5 ? "W" : "";
+ direction += diffX / distance < -0.5 ? "E" : "";
+ setSprite(direction, frameCount);
+
+ nekoPosX -= (diffX / distance) * nekoSpeed;
+ nekoPosY -= (diffY / distance) * nekoSpeed;
+
+ nekoPosX = Math.min(Math.max(16, nekoPosX), window.innerWidth - 16);
+ nekoPosY = Math.min(Math.max(16, nekoPosY), window.innerHeight - 16);
+
+ nekoEl.style.left = `${nekoPosX - 16}px`;
+ nekoEl.style.top = `${nekoPosY - 16}px`;
+ }
+
+ init();
+})();
diff --git a/assets/scripts/onekoswap.js b/assets/scripts/onekoswap.js
new file mode 100644
index 0000000..5c51d05
--- /dev/null
+++ b/assets/scripts/onekoswap.js
@@ -0,0 +1,15 @@
+document.addEventListener('DOMContentLoaded', () => {
+ var onekos = [
+ '/assets/oneko.gif',
+ '/assets/oneko-tux.gif',
+ // Add more file paths here if needed
+ ];
+ var currentIndex = 0;
+ document.getElementById('oneko-button').addEventListener('click', function() {
+ var oneko = document.getElementById('oneko');
+ currentIndex = (currentIndex + 1) % onekos.length;
+ var newOneko = onekos[currentIndex];
+ oneko.style.backgroundImage = 'url("' + newOneko + '")';
+ });
+
+});
diff --git a/assets/scripts/uptime.js b/assets/scripts/uptime.js
new file mode 100644
index 0000000..e1c36f3
--- /dev/null
+++ b/assets/scripts/uptime.js
@@ -0,0 +1,53 @@
+document.addEventListener('DOMContentLoaded', async function() {
+ async function fetchUptime() {
+ const uptimeElement = document.getElementById('uptime');
+ const names = {
+ "Zipline": "https://zip.quinten0508.com"
+ };
+
+ try {
+ const response = await fetch('https://quinten0508.com/api/uptime');
+ const data = await response.json();
+
+ // Clear previous content
+ uptimeElement.innerHTML = '';
+
+ data.forEach(element => {
+ // Create parent div for each pair
+ const pairDiv = document.createElement('div');
+ pairDiv.className = 'uptime-pair';
+
+ // Create div for name
+ const nameDiv = document.createElement('div');
+ nameDiv.className = 'uptime-name-element';
+
+ // Check if there is a specific URL for this monitor name
+ if (names.hasOwnProperty(element.monitor_name)) {
+ const url = names[element.monitor_name];
+ nameDiv.innerHTML = `${element.monitor_name}`;
+ } else {
+ nameDiv.textContent = element.monitor_name;
+ }
+
+ // Create div for status
+ const statusDiv = document.createElement('div');
+ statusDiv.className = 'uptime-status-element';
+ statusDiv.textContent = element.status;
+
+ // Append name and status divs to pair div
+ pairDiv.appendChild(nameDiv);
+ pairDiv.appendChild(statusDiv);
+
+ // Append pair div to uptime container
+ uptimeElement.appendChild(pairDiv);
+ });
+
+ } catch (error) {
+ console.error('Error fetching uptime data:', error);
+ }
+ }
+
+ // Fetch the uptime immediately and then every 10 seconds
+ fetchUptime();
+ setInterval(fetchUptime, 10000);
+});
diff --git a/colorscheme.css b/colorscheme.css
new file mode 100644
index 0000000..2694f08
--- /dev/null
+++ b/colorscheme.css
@@ -0,0 +1,18 @@
+
+/*
+COLOR SCHEME
+
+Colors
+#B1C6CB
+#74C1B1
+#8FC8B9
+#799ACD
+
+Dark-gray
+#262626
+
+Off-white
+#E9E9E9
+ #e9e9e9a2
+
+*/
diff --git a/favicon.ico b/favicon.ico
new file mode 100644
index 0000000..0c2a00c
Binary files /dev/null and b/favicon.ico differ
diff --git a/index.html b/index.html
new file mode 100644
index 0000000..6e51b48
--- /dev/null
+++ b/index.html
@@ -0,0 +1,104 @@
+
+
+
+
+ Quintens Outpost
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
Quinten's Outpost
+
+
+
+
+
+
+
+
+
+
Status
+
TEMPORARY DOWNTIME
+
Accidentally nuked all 700 server configuration files, expect wonky behaviour across services while i figure this out...
+
+
+
+
+
+
+
titleee
+
caption
+
this text will automatically fill up the box and has decent spacing??
+
Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.
+
+
+
+
+
heres some more text and shit
+
+
+
heres some more text and shit
+
+
+
heres some more text and shit
+
+
+
+
+
+

+
+
+
+
+
+
\ No newline at end of file
diff --git a/robots.txt b/robots.txt
new file mode 100644
index 0000000..77c4a55
--- /dev/null
+++ b/robots.txt
@@ -0,0 +1,33 @@
+User-agent: AdsBot-Google
+User-agent: Amazonbot
+User-agent: anthropic-ai
+User-agent: Applebot
+User-agent: AwarioRssBot
+User-agent: AwarioSmartBot
+User-agent: Bytespider
+User-agent: CCBot
+User-agent: ChatGPT-User
+User-agent: ClaudeBot
+User-agent: Claude-Web
+User-agent: cohere-ai
+User-agent: DataForSeoBot
+User-agent: Diffbot
+User-agent: FacebookBot
+User-agent: FriendlyCrawler
+User-agent: Google-Extended
+User-agent: GoogleOther
+User-agent: GPTBot
+User-agent: img2dataset
+User-agent: ImagesiftBot
+User-agent: magpie-crawler
+User-agent: Meltwater
+User-agent: omgili
+User-agent: omgilibot
+User-agent: peer39_crawler
+User-agent: peer39_crawler/1.0
+User-agent: PerplexityBot
+User-agent: PiplBot
+User-agent: scoop.it
+User-agent: Seekr
+User-agent: YouBot
+Disallow: /
\ No newline at end of file
diff --git a/stripes.svg b/stripes.svg
new file mode 100644
index 0000000..557c434
--- /dev/null
+++ b/stripes.svg
@@ -0,0 +1,23 @@
+
\ No newline at end of file
diff --git a/style.css b/style.css
new file mode 100644
index 0000000..3a355fa
--- /dev/null
+++ b/style.css
@@ -0,0 +1,457 @@
+/* ibm-plex-mono-regular - latin */
+@font-face {
+ font-display: swap; /* Check https://developer.mozilla.org/en-US/docs/Web/CSS/@font-face/font-display for other options. */
+ font-family: 'IBM Plex Mono';
+ font-style: normal;
+ font-weight: 400;
+ src: url('/assets/fonts/ibm-plex-mono-v19-latin-regular.woff2') format('woff2'); /* Chrome 36+, Opera 23+, Firefox 39+, Safari 12+, iOS 10+ */
+ }
+
+ /* ibm-plex-mono-italic - latin */
+ @font-face {
+ font-display: swap; /* Check https://developer.mozilla.org/en-US/docs/Web/CSS/@font-face/font-display for other options. */
+ font-family: 'IBM Plex Mono';
+ font-style: italic;
+ font-weight: 400;
+ src: url('/assets/fonts/ibm-plex-mono-v19-latin-italic.woff2') format('woff2'); /* Chrome 36+, Opera 23+, Firefox 39+, Safari 12+, iOS 10+ */
+ }
+
+ /* ibm-plex-mono-500 - latin */
+ @font-face {
+ font-display: swap; /* Check https://developer.mozilla.org/en-US/docs/Web/CSS/@font-face/font-display for other options. */
+ font-family: 'IBM Plex Mono';
+ font-style: normal;
+ font-weight: 500;
+ src: url('/assets/fonts/ibm-plex-mono-v19-latin-500.woff2') format('woff2'); /* Chrome 36+, Opera 23+, Firefox 39+, Safari 12+, iOS 10+ */
+ }
+
+ /* ibm-plex-mono-500italic - latin */
+ @font-face {
+ font-display: swap; /* Check https://developer.mozilla.org/en-US/docs/Web/CSS/@font-face/font-display for other options. */
+ font-family: 'IBM Plex Mono';
+ font-style: italic;
+ font-weight: 500;
+ src: url('/assets/fonts/ibm-plex-mono-v19-latin-500italic.woff2') format('woff2'); /* Chrome 36+, Opera 23+, Firefox 39+, Safari 12+, iOS 10+ */
+ }
+
+
+@media (max-width: 1400px) {
+ html {
+ font-size: 25px;
+ }
+ }
+
+
+ @media (max-width: 768px) {
+ html {
+ font-size: 16px;
+ }
+ }
+
+
+
+ @media (max-width: 450px) {
+ html {
+ font-size: 10px;
+ }
+ #subtitlebox{
+ height: 6rem !important;
+ }
+ #lastfm-box, #links {
+ flex: 25 !important;
+ }
+ #heartbeatbox, #clock-box {
+ flex: 13 !important;
+ }
+
+ }
+
+
+/*
+ Rules for thy Sanity
+ h1 = title
+ h3 = paragraph
+ h5 = caption
+ paragraph = text
+ 3rem vertical space between standard boxes (split into 1.5rem top and bottom)
+*/
+
+/*
+ ideas
+ * navbar as vertical element on left side
+ * status bar under title for lastfm, mood?, misc
+ * services uptime - uptime kuma integration ---- sad, no api or anything for kuma...
+ * socials/links/platforms in first right-aligned box
+ ** wont work well on mobile, instead just attach it under title within same box?
+ * left side of that box as bio
+ * current weather widget to right of lastfm status -- naur, too much effort and weird?
+ * comets and blinking stars in background
+ * more animation in general - make this mf ALIVE!!!!
+ * fancy borders see bookmark
+ * make a little place for the kitty in top left (or right?)
+*/
+
+/*
+The container query length units are:
+
+ cqw: 1% of a query container's width
+ cqh: 1% of a query container's height
+ cqi: 1% of a query container's inline size
+ cqb: 1% of a query container's block size
+ cqmin: The smaller value of either cqi or cqb
+ cqmax: The larger value of either cqi or cqb
+
+*/
+
+
+
+html {
+ background-color: #000000;
+ background-image: url('/assets/bg.gif');
+ background-repeat: repeat;
+ background-color: transparent;
+ margin: 0px;
+ border: 0px;
+ padding: 0px;
+ min-height: 100vh;
+ font-family: "IBM Plex Mono", monospace;
+ font-style: normal;
+ color: #E9E9E9;
+ font-weight: 400;
+}
+
+body {
+ margin: 0px;
+ border: 0px;
+ padding: 0px;
+ min-height: 100vh;
+
+}
+
+
+.container {
+ width: min(900px,92vw);
+ margin: auto;
+}
+
+#maintitlebox {
+ margin-top: 1rem;
+ margin-bottom: 0;
+}
+
+#maintitle {
+ font-size: 3.5rem;
+ text-align: center;
+ margin: 0;
+ margin: auto;
+}
+
+
+#subtitlebox {
+ display: flex;
+ justify-content: stretch;
+ align-items: stretch;
+ height: 4rem;
+ margin: 0;
+ padding: 0;
+ margin-bottom: 1.5rem;
+}
+
+ #links {
+ padding: 0;
+ margin: 0;
+ height: 100%;
+ flex: 1 1 auto;
+ display: flex;
+ justify-content: center;
+ align-items: center;
+ flex-wrap: wrap;
+ flex: 3;
+ }
+
+ .linkbox {
+ margin-right: 1rem;
+ background: #000;
+ padding: 0.5rem;
+
+ }
+ #heartbeatbox {
+ margin: 0;
+ padding: 0;
+ height: 100%;
+ display: flex;
+ justify-content: center;
+ align-items: center;
+ flex: 1
+
+ }
+ #heartbeat-time {
+ font-size: 1.2rem;
+ }
+
+.link {
+ color: #E9E9E9;
+ text-decoration: none;
+ position: relative;
+}
+
+ .link::after {
+ content: '';
+ position: absolute;
+ width: 0;
+ height: 0.1rem;
+ display: block;
+ left: 0;
+ background: #E9E9E9;
+ transition: width 0.3s ease, left 0.3s ease;
+ }
+
+ .link:hover::after {
+ width: 100%;
+ left: 0;
+ }
+
+
+.box {
+ background: #000;
+ border: 1px solid #E9E9E9;
+ margin-top: 1.5rem;
+ margin-bottom: 1.5rem;
+ padding: 1rem;
+ align-self: center;
+ justify-self: center;
+ max-height: 100%;
+}
+
+
+.button {
+ background: #000;
+ border: 1px solid #E9E9E9;
+ margin-top: 1.5rem;
+ margin-bottom: 1.5rem;
+ padding: 1rem;
+ align-self: center;
+ justify-self: center;
+ max-height: 100%;
+ cursor: pointer;
+ display: inline-block;
+ text-align: center;
+ text-decoration: none;
+ color: inherit;
+ font: inherit;
+}
+
+.button:active {
+ background: #333333; /* Lighter grey color */
+}
+
+
+
+.box:last-child {
+ margin-bottom: 3rem;
+}
+
+
+#header {
+ display: flex;
+ flex-direction: row;
+ margin-top: 0.75rem;
+ height: 8rem;
+}
+
+
+
+ #lastfm-box {
+ background: #000;
+ border: 1px solid #E9E9E9;
+ justify-content: space-between;
+ display: flex;
+ align-items: center;
+ flex: 1 1 auto;
+ height: 8rem;
+ flex: 3;
+ }
+
+ #lastfm-contents {
+ font-family: "IBM Plex Mono", monospace;
+ font-weight: 400;
+ font-style: normal;
+ color: #E9E9E9;
+ line-height: 1.8;
+ font-size: min(1rem, 2.2cm);
+ overflow: hidden;
+ max-height: 100%; /* Adjusted to 100% */
+ padding: 1rem;
+ padding-top: 0.5rem;
+ display: block;
+ box-sizing: border-box;
+ }
+
+
+
+
+ #lastfm-artist {
+ font-style: normal;
+ }
+ #lastfm-timesinceplay {
+ font-style:italic;
+ color:#e9e9e9a2;
+ font-size: min(1rem, 2.2cqmin);
+ }
+
+ #lastfm-lastplayedsong, #lastfm-lastlastplayedsong{
+ font-style:italic;
+ color: #e9e9e9a2;
+ font-size: min(1rem, 2.2cqmin);
+ }
+ #lastfm-artbox {
+ padding: 1rem;
+ }
+ #lastfm-art {
+ border: 1px solid #E9E9E9;
+ padding: 0.2rem;
+ width: 5rem;
+ height: 5rem;
+ }
+
+
+ #clock-box {
+ background: #000;
+ border: 1px solid #E9E9E9;
+ display: flex;
+ flex-direction: column;
+ align-items: center;
+ justify-content: center;
+ height: 8rem;
+ gap: 0.3rem;
+ flex: 1;
+ }
+ #clocktitle {
+ padding: 0;
+ margin: 0;
+ font-size: 0.9rem;
+ }
+
+ #clock-contents {
+ display: flex;
+ gap: 0.3rem;
+ align-items: top;
+ justify-content: center;
+ }
+ #clock {
+ padding: 0;
+ margin: 0;
+ font-size: 1.8rem;
+ flex: 9;
+ padding-left: 0.5rem;
+ }
+ #clock-icon {
+ padding: 0;
+ margin: 0;
+ padding-top: 0.4rem;
+ flex: 1;
+ width: 1.2rem;
+ height: 1.2rem;
+ padding-right: 0.5rem;
+ }
+
+
+
+
+.image {
+ width: 100%;
+ border: 1rem;
+ padding-top: 1rem;
+}
+
+
+#uptime {
+ display: flex;
+ align-items: stretch;
+ justify-content: left;
+ flex-direction: column;
+}
+
+.uptime-pair {
+ display: flex;
+ border: 1px solid #E9E9E9;
+ padding: 1rem;
+ flex-direction: row;
+}
+.uptime-name-element, .uptime-status-element {
+ border: 1px solid red;
+ padding: 1rem;
+}
+
+/* #uptime-name, #uptime-status {
+ align-items: center;
+ height: 100%;
+ margin: 0;
+
+ }
+ .uptime-name-element, .uptime-status-element {
+ height: 2rem;
+ padding: 0;
+ margin: 0;
+ border: 0;
+ }
+ .uptime-name-element {
+ font-style: bold;
+ border-bottom: 1px solid #E9E9E9;
+ }
+ .uptime-name-element:last-of-type {
+ border-bottom: 0;
+ }
+ .uptime-box {
+ background: #000;
+ border: 1px solid #E9E9E9;
+ margin-top: 1.5rem;
+ margin-bottom: 1.5rem;
+ padding: 1rem;
+ max-height: 100%;
+ }
+*/
+
+
+
+
+h1 {
+ font-weight: 500;
+ font-size: 4rem;
+ letter-spacing: 0.2rem;
+ margin: 0;
+ padding: 0;
+}
+
+h3 {
+ font-weight: 500;
+ font-size: 2.4rem;
+ margin: 0;
+ padding: 0;
+ padding-bottom: 0.2rem;
+ text-align: left;
+}
+
+h5 {
+ font-size: 1.2rem;
+ font-style: italic;
+ margin: 0;
+ padding: 0;
+ padding-bottom: 1.2rem;
+}
+
+p, pre {
+ font-size: 1.2rem;
+ margin: 0;
+ padding: 0;
+}
+
+button {
+ color: #E9E9E9;
+}
+
+
+#stripes {
+ width: 100vw;
+ height: max(16vh, 176px);
+}
\ No newline at end of file