diff --git a/assets/index.css b/assets/index.css new file mode 100644 index 0000000..5e4720b --- /dev/null +++ b/assets/index.css @@ -0,0 +1,576 @@ +:root { + --bg-color: #0a0a0a; + --border-color: #2a5a5a; + --text-muted: #88aaaa; + --text-bright: #cef0f0; + --glow-color: rgba(42, 90, 90, 0.4); +} + +@keyframes pulse { + 0% { + box-shadow: 0 0 5px var(--glow-color); + opacity: 0.7; + } + 50% { + box-shadow: 0 0 10px var(--glow-color); + opacity: 1; + } + 100% { + box-shadow: 0 0 5px var(--glow-color); + opacity: 0.7; + } +} + +@keyframes slideInBorder { + 0% { + width: 0; + opacity: 0.2; + } + 100% { + width: 100%; + opacity: 1; + } +} + +html { + scroll-snap-type: y mandatory; + scroll-behavior: smooth; + -webkit-user-select: none; + -moz-user-select: none; + -ms-user-select: none; + user-select: none; +} + +body { + background: var(--bg-color); + color: var(--text-bright); + font-family: "Courier New", monospace; + margin: 0; + padding: 0; + min-height: 100vh; + position: relative; + overflow-x: hidden; +} + +body::before { + content: ""; + position: fixed; + top: 0; + left: 0; + right: 0; + bottom: 0; + background: repeating-linear-gradient( + 0deg, + rgba(0, 0, 0, 0.1), + rgba(0, 0, 0, 0.1) 1px, + transparent 1px, + transparent 2px + ); + pointer-events: none; + z-index: 1000; +} + +.terminal { + margin-left: 80px; + padding: 20px; + max-width: 800px; + position: relative; +} + +.terminal::before { + content: ""; + position: absolute; + top: 0; + left: 0; + right: 0; + bottom: 0; + border: 1px solid var(--border-color); + pointer-events: none; + opacity: 0.5; + clip-path: polygon(0 2%, 98% 0, 100% 98%, 2% 100%); +} + +.sidebar { + position: fixed; + left: 0; + top: 0; + bottom: 0; + width: 80px; + background: var(--bg-color); + border-right: 1px solid var(--border-color); + display: flex; + flex-direction: column; + align-items: center; + padding-top: 20px; + z-index: 1000; + box-shadow: 1px 0 15px var(--glow-color); + clip-path: polygon(0 0, 90% 0, 100% 2%, 100% 98%, 90% 100%, 0 100%); +} + +.nav-icon { + color: var(--text-muted); + cursor: pointer; + margin: 20px 0; + text-decoration: none; + position: relative; + display: flex; + flex-direction: column; + align-items: center; +} + +.nav-icon .icon { + font-size: 24px; + transition: color 0.3s ease; +} + +.nav-icon .nav-label { + position: absolute; + top: 100%; + left: 50%; + transform: translateX(-50%); + font-size: 10px; + opacity: 0; + transition: + opacity 0.3s ease, + transform 0.3s ease; + white-space: nowrap; + margin-top: 5px; + pointer-events: none; + color: var(--text-muted); +} + +.nav-icon.active .nav-label { + opacity: 1; + transform: translateX(-50%) translateY(0); + color: var(--text-muted); +} + +.nav-icon:hover .nav-label { + opacity: 1; + transform: translateX(-50%) translateY(0); +} + +.nav-icon:hover .icon { + color: var(--text-bright); +} + +.nav-icon.active .icon { + color: var(--text-bright); +} + +.nav-icon.active { + color: var(--text-bright); + position: relative; +} + +.nav-icon:hover { + color: var(--text-bright); +} + +.section { + height: 100vh; + padding: 20px 0; + scroll-snap-align: start; + scroll-snap-stop: always; + position: relative; + overflow-y: auto; +} + +#contact { + height: auto; + min-height: 100vh; +} + +.section-title { + color: var(--text-muted); + margin-bottom: 20px; + font-size: 1.2em; + display: flex; + align-items: center; + position: relative; + padding-left: 20px; +} + +.section-title::before { + content: "◈"; + position: absolute; + left: 0; + bottom: 1px; + color: var(--text-bright); + text-shadow: 0 0 10px var(--glow-color); + background: none; +} + +.header { + border-bottom: none; + padding-bottom: 10px; + margin-bottom: 20px; + position: relative; +} + +.header::after { + content: ""; + position: absolute; + bottom: -1px; + left: 0; + width: 100%; + height: 1px; + background: linear-gradient( + 90deg, + transparent, + var(--text-bright), + transparent + ); + animation: + pulse 2s infinite, + slideInBorder 1s ease-out; +} + +.timestamp { + font-size: 0.9em; + color: var(--text-muted); + font-family: "Courier New", monospace; + line-height: 1; +} + +.location { + margin-top: 4px; + color: var(--text-muted); + padding-bottom: 5px; +} + +.entry-list { + margin-top: 10px; +} + +.nested-entries { + margin-left: 20px; + margin-top: 5px; + border-left: 1px solid var(--border-color); + padding-left: 15px; + position: relative; +} + +.nested-entries::before { + content: ""; + position: absolute; + left: -1px; + top: 0; + bottom: 0; + width: 1px; + background: var(--border-color); + box-shadow: 0 0 8px var(--glow-color); +} + +.entry { + margin: 10px 0; + padding-left: 20px; + position: relative; +} + +.entry::before { + content: "▹"; + position: absolute; + left: 0; + margin-right: 10px; +} + +.entry-link { + color: inherit; + text-decoration: none; + transition: all 0.3s ease; + position: relative; +} + +.entry-link:hover { + color: var(--text-bright); +} + +.entry-link::after { + content: ""; + position: absolute; + bottom: -2px; + left: 0; + width: 100%; + height: 2px; + background: linear-gradient( + 90deg, + transparent, + var(--text-bright), + var(--text-bright), + transparent + ); + transform: scaleX(0); + transform-origin: right; + transition: all 0.4s cubic-bezier(0.23, 1, 0.32, 1); + box-shadow: 0 0 8px var(--glow-color); + opacity: 0.8; +} + +.entry-link:hover::after { + transform: scaleX(1); + transform-origin: left; +} + +.project-item, +.status-item { + border: 1px solid var(--border-color); + padding: 15px; + margin: 15px 0; + cursor: pointer; + transition: + border-color 0.3s ease, + background-color 0.3s ease, + box-shadow 0.3s ease, + transform 0.3s ease; + position: relative; + background: linear-gradient( + 135deg, + rgba(42, 90, 90, 0.1), + rgba(42, 90, 90, 0.2) + ); + clip-path: polygon( + 0 0, + 98% 0, + 100% 15px, + 100% 100%, + 2% 100%, + 0 calc(100% - 15px) + ); + transform: translateZ(20px); + box-shadow: + 0 5px 15px rgba(0, 0, 0, 0.4), + 0 10px 20px rgba(42, 90, 90, 0.3), + inset 0 1px rgba(255, 255, 255, 0.2), + inset -2px -2px 4px rgba(0, 0, 0, 0.5); + transform-style: preserve-3d; + perspective: 1000px; + backdrop-filter: blur(5px); + border-left: 3px solid rgba(42, 90, 90, 0.6); + border-top: 2px solid rgba(255, 255, 255, 0.15); +} + +.project-item:hover, +.status-item:hover { + border-color: var(--text-bright); + background: rgba(42, 90, 90, 0.1); + box-shadow: + 0 5px 15px rgba(0, 0, 0, 0.3), + 0 10px 20px rgba(42, 90, 90, 0.2), + inset 0 1px rgba(255, 255, 255, 0.2); + transform: translateY(-2px) translateZ(10px); + animation: pulse 2s infinite; +} + +.project-header { + display: flex; + justify-content: space-between; + align-items: center; +} + +.project-header .entry::before { + content: "◈"; + color: var(--text-muted); + text-shadow: 0 0 5px var(--glow-color); + transition: all 0.3s ease; +} + +.project-item:hover .project-header .entry::before { + color: var(--text-bright); + text-shadow: 0 0 10px var(--glow-color); + transform: rotate(45deg); +} + +.expand-icon { + color: var(--text-muted); + transition: transform 0.3s ease; +} + +.project-item.expanded .expand-icon { + transform: rotate(45deg); +} + +.project-content { + max-height: 0; + overflow: hidden; + transition: + max-height 0.5s cubic-bezier(0.4, 0, 0.2, 1), + opacity 0.3s ease, + transform 0.3s ease; + margin-top: 10px; + opacity: 0; + transform: translateY(-10px); +} + +.project-content.expanded { + max-height: 500px; + opacity: 1; + transform: translateY(0); +} + +.project-link { + display: inline-block; + color: var(--text-bright); + text-decoration: none; + padding: 10px 15px; + border: 1px solid var(--border-color); + transition: + border-color 0.3s ease, + background-color 0.3s ease; + background: rgba(42, 90, 90, 0.05); + margin-top: 10px; + position: relative; + overflow: hidden; +} + +.project-link:hover { + border-color: var(--text-bright); + background: rgba(42, 90, 90, 0.1); +} + +.status-grid { + display: grid; + gap: 15px; +} + +.status-indicator { + display: inline-block; + width: 8px; + height: 8px; + border-radius: 50%; + margin-right: 10px; +} + +.status-online { + background-color: #4caf50; + box-shadow: 0 0 10px rgba(76, 175, 80, 0.3); +} + +.status-offline { + background-color: #f44336; + box-shadow: 0 0 10px rgba(244, 67, 54, 0.3); +} + +.status-warning { + background-color: #ff9800; + box-shadow: 0 0 10px rgba(255, 152, 0, 0.3); +} + +.status-details { + max-height: 0; + overflow: hidden; + transition: + max-height 0.5s cubic-bezier(0.4, 0, 0.2, 1), + opacity 0.3s ease, + transform 0.3s ease; + margin-top: 10px; + opacity: 0; + transform: translateY(-10px); +} + +.status-details.expanded { + max-height: 500px; + opacity: 1; + transform: translateY(0); +} + +.contact-links { + display: flex; + flex-direction: column; + gap: 20px; + margin-top: 20px; +} + +.contact-link { + display: flex; + align-items: center; + color: var(--text-bright); + text-decoration: none; + padding: 15px; + border: 1px solid var(--border-color); + transition: + border-color 0.3s ease, + background-color 0.3s ease; + background: rgba(42, 90, 90, 0.05); + clip-path: polygon( + 0 0, + 98% 0, + 100% 15px, + 100% 100%, + 2% 100%, + 0 calc(100% - 15px) + ); +} + +.contact-link:hover { + border-color: var(--text-bright); + background: rgba(42, 90, 90, 0.1); +} + +.contact-icon { + margin-right: 15px; + font-size: 24px; +} + +@media (max-width: 768px) { + .terminal { + margin-left: 60px; + padding: 15px; + } + + .sidebar { + width: 60px; + } + + .nav-icon { + font-size: 20px; + } + + .project-item, + .contact-link, + .status-item { + padding: 10px; + } +} + +@media (max-width: 480px) { + .terminal { + margin-left: 50px; + padding: 10px; + } + + .sidebar { + width: 50px; + } + + .nav-icon { + font-size: 18px; + margin: 15px 0; + } + + .section-title { + font-size: 1em; + } +} + +.status-average { + font-size: 0.9em; + color: var(--text-muted); + font-family: "Courier New", monospace; + margin-top: 4px; + color: var(--text-muted); +} + +.header-metrics { + display: flex; + align-items: baseline; + gap: 10px; + color: var(--text-muted); + font-size: 0.9em; + font-family: "Courier New", monospace; + line-height: 1; +} + +.separator { + color: var(--border-color); +} diff --git a/assets/index.js b/assets/index.js new file mode 100644 index 0000000..170e25f --- /dev/null +++ b/assets/index.js @@ -0,0 +1,218 @@ +// this is slop. i know. i'm sorry. +// atleast i'm not a reactfag. + +// we add some fancy text typing animation blah blah blah +function writeoutnavlabel() { + const firstNavIcon = document.querySelector('.nav-icon[href="#about"]'); + const label = firstNavIcon.querySelector(".nav-label"); + const text = label.textContent; + label.textContent = ""; + label.style.opacity = "1"; + + let i = 0; + function type() { + if (i < text.length) { + label.textContent += text.charAt(i); + i++; + setTimeout(type, 100); + } + } + // why am i using a function inside a function? + // i don't know. i'm sorry. + type(); +} + +function updateTime() { + const now = new Date(); + const timeString = now + .toLocaleString("en-US", { + month: "2-digit", + day: "2-digit", + year: "numeric", + hour: "2-digit", + minute: "2-digit", + second: "2-digit", + fractionalSecondDigits: 3, + hour12: true, + }) + .toUpperCase(); + document.getElementById("current-time").textContent = timeString; +} + +updateTime(); +// this is a terrible way to update the time +setInterval(updateTime, 1); + +function calculateAverageUptime(services) { + if (!services || services.length === 0) return 0; + // did i ever mention that i hate math? + const totalUptime = services.reduce( + (sum, service) => sum + service.online_24_hours, + 0, + ); + return (totalUptime / services.length).toFixed(2); +} + +// projects +const projectItems = document.querySelectorAll(".project-item"); +projectItems.forEach((item) => { + item.addEventListener("click", function () { + const content = this.querySelector(".project-content"); + const icon = this.querySelector(".expand-icon"); + + // close other expanded items + projectItems.forEach((otherItem) => { + if (otherItem !== item) { + otherItem + .querySelector(".project-content") + .classList.remove("expanded"); + otherItem.querySelector(".expand-icon").textContent = "+"; + } + }); + content.classList.toggle("expanded"); + icon.textContent = content.classList.contains("expanded") ? "−" : "+"; + }); +}); + +// status updater +function formatLatency(latency) { + return (Math.round((latency / 1000) * 10) / 10).toFixed(1); +} + +function formatUptime(uptime) { + return uptime.toFixed(2); +} + +function getStatusClass(service) { + if (!service.online) return "status-offline"; + if (service.latency > 500000) return "status-warning"; + return "status-online"; +} + +function getGroupName(groupId) { + const groupNames = { + 4: "INFRASTRUCTURE", + 5: "SERVICES", + 6: "API", + }; + return groupNames[groupId] || `GROUP ${groupId}`; +} + +function createStatusHTML(services) { + // Group services by group_id + const groups = services.reduce((acc, service) => { + const groupId = service.group_id; + if (!acc[groupId]) acc[groupId] = []; + acc[groupId].push(service); + return acc; + }, {}); + + return Object.entries(groups) + .map( + ([groupId, groupServices]) => ` +
+ Open-Source Language Model interface for use in web + browsers. +
++ Language model interface designed for autonomous + agents that utilize the internet. +
++ Discord music bot utilizing Jellyfin to play your + own music at crisp 96kbps Opus quality. +
++ Monitor internet connectivity in regions of war and + conflict. +
+