katwebsite/index.html

2107 lines
72 KiB
HTML

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>katOS — oiupoyt</title>
<!-- Google AdSense -->
<script async src="https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-3876936176422477" crossorigin="anonymous"></script>
<!-- Fonts & Icons: Space Mono & Share Tech Mono nerdy aesthetic -->
<link rel="preconnect" href="https://fonts.googleapis.com">
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<link href="https://fonts.googleapis.com/css2?family=Share+Tech+Mono&family=Space+Mono:ital,wght@0,400;0,700;1,400&family=JetBrains+Mono:wght@400;500&display=swap" rel="stylesheet">
<link href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.5.0/css/all.min.css" rel="stylesheet">
<style>
/* ── Reset & Brutalist Geometry ─────────────────────────────────────────── */
* {
box-sizing: border-box;
margin: 0;
padding: 0;
border-radius: 0 !important; /* Strict squared-out terminals & windows */
scrollbar-width: none !important;
-ms-overflow-style: none !important;
}
body, button, input, textarea, select, pre, code, p, a, span, div, h1, h2, h3, h4, h5, h6 {
font-family: 'Space Mono', 'Share Tech Mono', monospace;
}
/* Preserve Font Awesome Icons strictly */
.fa, .fas, .far, .fal, .fad, i[class*="fa-"] {
font-family: "Font Awesome 6 Free" !important;
font-weight: 900 !important;
}
.fab, i.fab {
font-family: "Font Awesome 6 Brands" !important;
font-weight: 400 !important;
}
:root {
--bg: #000000;
--panel-bg: rgba(6, 6, 10, 0.22); /* High glass transparency */
--panel-active: rgba(10, 10, 16, 0.35); /* Focused transparency */
--border: rgba(255, 255, 255, 0.12);
--border-hover: rgba(255, 255, 255, 0.35);
--border-active: rgba(255, 255, 255, 0.7);
--text: #f0f0f0;
--text-dim: #666666;
--text-sub: #999999;
--accent: #ffffff;
--glow: 0 0 10px rgba(255, 255, 255, 0.8), 0 0 22px rgba(255, 255, 255, 0.4);
--glow-subtle: 0 0 8px rgba(255, 255, 255, 0.4);
--taskbar-h: 36px;
}
body {
background: var(--bg);
color: var(--text);
font-family: 'Space Mono', 'Share Tech Mono', monospace;
height: 100vh;
width: 100vw;
overflow: hidden;
user-select: none;
-webkit-font-smoothing: antialiased;
}
/* ── Background Engine & Tactical CRT Layer ────────────────────────────── */
.video-bg {
position: fixed;
inset: 0;
width: 100%;
height: 100%;
object-fit: cover;
z-index: 0;
pointer-events: none;
opacity: 0;
display: none;
transition: opacity 0.5s ease;
filter: contrast(1.15) brightness(0.88);
}
#bgCanvas {
position: fixed;
inset: 0;
width: 100%;
height: 100%;
pointer-events: none;
z-index: 1;
}
.crt-overlay {
position: fixed;
inset: 0;
width: 100%;
height: 100%;
pointer-events: none;
z-index: 2;
background:
radial-gradient(ellipse at center, rgba(0, 0, 0, 0) 50%, rgba(0, 0, 0, 0.72) 100%),
repeating-linear-gradient(0deg, rgba(255, 255, 255, 0.012) 0px, rgba(255, 255, 255, 0.012) 1px, transparent 1px, transparent 3px);
}
/* Minimalist On-Screen HUD Notification Toast */
.kat-toast {
position: fixed;
bottom: 48px;
left: 50%;
transform: translateX(-50%) translateY(8px);
background: rgba(10, 10, 14, 0.92);
border: 1px solid rgba(255, 255, 255, 0.35);
box-shadow: 0 0 20px rgba(255, 255, 255, 0.25);
color: #ffffff;
padding: 6px 16px;
font-size: 11px;
letter-spacing: 0.08em;
text-transform: uppercase;
z-index: 99999;
pointer-events: none;
opacity: 0;
transition: all 0.22s ease;
}
.kat-toast.show {
opacity: 1;
transform: translateX(-50%) translateY(0);
}
/* ── Desktop Workspace ───────────────────────────────────────────────────── */
.desktop {
position: relative;
width: 100%;
height: calc(100vh - var(--taskbar-h));
z-index: 2;
overflow: hidden;
}
/* ── Transparent Squared Windows ─────────────────────────────────────────── */
.window {
position: absolute;
background: var(--panel-bg);
border: 1px solid var(--border);
backdrop-filter: blur(14px) saturate(160%);
-webkit-backdrop-filter: blur(14px) saturate(160%);
box-shadow: 0 10px 40px rgba(0, 0, 0, 0.6), 0 0 1px rgba(255, 255, 255, 0.15) inset;
z-index: 10;
display: flex;
flex-direction: column;
transition: border-color 0.18s ease, box-shadow 0.18s ease, background 0.18s ease;
}
.window.active {
border-color: var(--border-active);
background: var(--panel-active);
box-shadow: 0 14px 50px rgba(0, 0, 0, 0.75), 0 0 14px rgba(255, 255, 255, 0.18), 0 0 1px rgba(255, 255, 255, 0.4) inset;
z-index: 30;
}
.window.minimized {
display: none !important;
}
.window.maximized {
top: 0 !important;
left: 0 !important;
width: 100% !important;
height: 100% !important;
}
/* Titlebar */
.titlebar {
height: 30px;
min-height: 30px;
background: rgba(255, 255, 255, 0.03);
border-bottom: 1px solid var(--border);
padding: 0 10px;
font-size: 11px;
letter-spacing: 0.04em;
cursor: grab;
display: flex;
align-items: center;
justify-content: space-between;
user-select: none;
}
.window.active .titlebar {
background: rgba(255, 255, 255, 0.06);
border-bottom-color: var(--border-active);
}
.titlebar:active {
cursor: grabbing;
}
.title-left {
display: flex;
align-items: center;
gap: 8px;
color: var(--text-sub);
font-weight: 500;
font-size: 11px;
}
.window.active .title-left {
color: #ffffff;
text-shadow: var(--glow-subtle);
}
.title-left i {
font-size: 10px;
opacity: 0.7;
}
/* Remade Minimalist Terminal Navigation Dot */
.window-controls {
display: flex;
align-items: center;
justify-content: center;
}
.win-dot-close {
width: 14px;
height: 14px;
border: 1px solid rgba(255, 255, 255, 0.22);
background: rgba(255, 255, 255, 0.03);
cursor: pointer;
display: flex;
align-items: center;
justify-content: center;
transition: all 0.16s ease;
position: relative;
}
.win-dot-close::before {
content: '';
width: 4px;
height: 4px;
background: rgba(255, 255, 255, 0.55);
box-shadow: 0 0 4px rgba(255, 255, 255, 0.35);
transition: all 0.16s ease;
}
.win-dot-close:hover {
border-color: #ffffff;
background: rgba(255, 255, 255, 0.16);
box-shadow: 0 0 10px rgba(255, 255, 255, 0.45);
}
.win-dot-close:hover::before {
background: #ffffff;
width: 6px;
height: 6px;
box-shadow: 0 0 8px #ffffff;
}
/* Window Body */
.window-body {
flex: 1;
padding: 14px 16px;
font-size: 12px;
line-height: 1.6;
overflow-y: auto;
overflow-x: hidden;
color: #d0d0d0;
background: transparent !important;
}
/* Complete scrollbar removal (clean invisible scrolling) */
* {
scrollbar-width: none !important;
-ms-overflow-style: none !important;
}
*::-webkit-scrollbar,
.window-body::-webkit-scrollbar,
.terminal-body::-webkit-scrollbar,
body::-webkit-scrollbar,
.desktop::-webkit-scrollbar {
display: none !important;
width: 0 !important;
height: 0 !important;
}
/* ── Terminal Styling ────────────────────────────────────────────────────── */
.terminal-body {
font-family: 'Space Mono', 'Share Tech Mono', monospace !important;
font-size: 11px;
line-height: 1.48;
padding: 12px 14px;
color: #e5e5e5;
background: transparent !important;
overflow-y: auto;
overflow-x: hidden;
height: 100%;
user-select: text;
}
.fastfetch-pre {
white-space: pre;
line-height: 1.35;
letter-spacing: 0.2px;
margin-bottom: 12px;
display: block;
}
.term-row {
display: flex;
align-items: center;
gap: 6px;
}
.term-prompt-line {
display: flex;
align-items: center;
gap: 6px;
margin-top: 8px;
}
.term-prompt {
color: #ffffff;
text-shadow: var(--glow-subtle);
font-weight: 600;
flex-shrink: 0;
}
.term-input {
flex: 1;
background: transparent;
border: none;
color: #ffffff;
font-family: inherit;
font-size: 11px;
outline: none;
caret-color: #fff;
}
.term-output {
margin-top: 4px;
margin-bottom: 8px;
color: #aaa;
line-height: 1.5;
white-space: pre-wrap;
}
.term-cmd {
color: #fff;
font-weight: 600;
}
/* Links & Lists in Windows */
.link-item {
display: flex;
align-items: center;
gap: 10px;
padding: 8px 10px;
margin-bottom: 6px;
background: rgba(255, 255, 255, 0.02);
border: 1px solid rgba(255, 255, 255, 0.09);
cursor: pointer;
font-size: 11px;
color: #ddd;
transition: all 0.15s ease;
text-decoration: none;
}
.link-item:last-child {
margin-bottom: 0;
}
.link-item:hover {
background: rgba(255, 255, 255, 0.09);
border-color: #ffffff;
color: #fff;
box-shadow: 0 0 10px rgba(255, 255, 255, 0.15);
transform: translateX(3px);
}
.link-item i {
width: 16px;
text-align: center;
color: #ffffff;
opacity: 0.85;
}
.link-item .badge {
margin-left: auto;
font-size: 10px;
color: var(--text-dim);
border: 1px solid rgba(255, 255, 255, 0.15);
padding: 1px 6px;
background: rgba(0, 0, 0, 0.6);
}
.skill-tag {
display: inline-block;
padding: 3px 8px;
margin: 3px;
background: rgba(255, 255, 255, 0.03);
border: 1px solid rgba(255, 255, 255, 0.1);
font-size: 11px;
color: #ccc;
transition: all 0.15s ease;
}
.skill-tag:hover {
border-color: #ffffff;
color: #ffffff;
box-shadow: 0 0 8px rgba(255, 255, 255, 0.3);
}
/* ── Visualizer Container ────────────────────────────────────────────────── */
.viz-container {
width: 100%;
height: 100%;
position: relative;
background: transparent !important;
overflow: hidden;
display: flex;
align-items: center;
justify-content: center;
}
#vizCanvas {
width: 100%;
height: 100%;
display: block;
background: transparent;
cursor: pointer;
}
.viz-overlay {
position: absolute;
top: 10px;
left: 12px;
display: flex;
align-items: center;
gap: 10px;
pointer-events: none;
font-size: 10px;
letter-spacing: 0.08em;
color: rgba(255, 255, 255, 0.4);
text-transform: uppercase;
}
.viz-mode-badge {
padding: 2px 6px;
border: 1px solid rgba(255, 255, 255, 0.3);
background: rgba(0, 0, 0, 0.6);
color: #ffffff;
text-shadow: var(--glow-subtle);
}
.viz-hint {
position: absolute;
bottom: 10px;
right: 12px;
font-size: 9px;
color: rgba(255, 255, 255, 0.25);
letter-spacing: 0.06em;
text-transform: uppercase;
pointer-events: none;
}
.viz-idle-msg {
position: absolute;
color: rgba(255, 255, 255, 0.25);
font-size: 11px;
letter-spacing: 0.15em;
text-transform: uppercase;
pointer-events: none;
transition: opacity 0.3s ease;
}
/* ── Taskbar ─────────────────────────────────────────────────────────────── */
.taskbar {
position: fixed;
bottom: 0;
left: 0;
width: 100%;
height: var(--taskbar-h);
background: rgba(4, 4, 6, 0.94);
border-top: 1px solid var(--border);
backdrop-filter: blur(20px);
z-index: 100;
display: flex;
align-items: center;
justify-content: space-between;
padding: 0 12px;
font-size: 11px;
}
.taskbar-left {
display: flex;
align-items: center;
gap: 10px;
height: 100%;
}
/* Clean KatOS button without icon */
.os-logo-btn {
font-family: 'JetBrains Mono', monospace;
font-weight: 700;
font-size: 12px;
letter-spacing: 0.05em;
color: #ffffff;
text-shadow: var(--glow-subtle);
padding: 4px 10px;
border: 1px solid rgba(255, 255, 255, 0.25);
background: rgba(255, 255, 255, 0.05);
cursor: pointer;
display: flex;
align-items: center;
transition: all 0.15s ease;
}
.os-logo-btn:hover {
background: rgba(255, 255, 255, 0.15);
border-color: #ffffff;
box-shadow: var(--glow-subtle);
}
.taskbar-divider {
width: 1px;
height: 16px;
background: rgba(255, 255, 255, 0.12);
margin: 0 2px;
}
.taskbar-windows {
display: flex;
align-items: center;
gap: 4px;
height: 100%;
}
.taskbar-win-btn {
display: inline-flex;
align-items: center;
gap: 6px;
padding: 4px 10px;
background: rgba(255, 255, 255, 0.02);
border: 1px solid rgba(255, 255, 255, 0.1);
font-size: 11px;
color: var(--text-sub);
cursor: pointer;
max-width: 140px;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
transition: all 0.15s ease;
}
.taskbar-win-btn:hover {
background: rgba(255, 255, 255, 0.08);
color: #fff;
border-color: var(--border-hover);
}
.taskbar-win-btn.active {
background: rgba(255, 255, 255, 0.12);
border-color: #ffffff;
color: #fff;
box-shadow: var(--glow-subtle);
}
.taskbar-right {
display: flex;
align-items: center;
gap: 14px;
}
/* Music Player Widget in Taskbar */
.music-bar {
display: flex;
align-items: center;
gap: 8px;
padding: 2px 8px;
border: 1px solid rgba(255, 255, 255, 0.1);
background: rgba(255, 255, 255, 0.02);
}
.music-icon {
font-size: 11px;
color: #ffffff;
animation: spinDisc 3s linear infinite;
}
.music-icon.paused {
animation-play-state: paused;
color: var(--text-dim);
}
@keyframes spinDisc {
from { transform: rotate(0deg); }
to { transform: rotate(360deg); }
}
.music-track {
max-width: 160px;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
font-size: 10px;
color: var(--text-sub);
}
.player-btn {
background: transparent;
border: 1px solid rgba(255, 255, 255, 0.2);
color: #fff;
width: 20px;
height: 20px;
display: inline-flex;
align-items: center;
justify-content: center;
cursor: pointer;
font-size: 9px;
transition: all 0.15s ease;
}
.player-btn:hover {
background: #fff;
color: #000;
box-shadow: var(--glow-subtle);
}
.volume-slider {
width: 50px;
height: 3px;
-webkit-appearance: none;
appearance: none;
background: rgba(255, 255, 255, 0.25);
outline: none;
}
.volume-slider::-webkit-slider-thumb {
-webkit-appearance: none;
appearance: none;
width: 8px;
height: 12px;
background: #ffffff;
cursor: pointer;
}
/* System Tray Clock */
.tray-time {
color: #ffffff;
font-weight: 600;
letter-spacing: 0.05em;
text-shadow: var(--glow-subtle);
}
/* ── Minimal Boot Screen ─────────────────────────────────────────────────── */
#loadingScreen {
position: fixed;
inset: 0;
background: #000000;
z-index: 10000;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
cursor: pointer;
transition: opacity 0.5s ease;
}
#loadingScreen.hidden {
opacity: 0;
pointer-events: none;
}
.boot-minimal {
text-align: center;
}
.boot-logo {
font-family: 'JetBrains Mono', monospace;
font-size: 52px;
font-weight: 700;
letter-spacing: -0.04em;
color: #ffffff;
text-shadow: 0 0 24px rgba(255, 255, 255, 0.8), 0 0 45px rgba(255, 255, 255, 0.3);
margin-bottom: 12px;
}
.boot-sub {
font-size: 11px;
color: #777777;
letter-spacing: 0.12em;
text-transform: lowercase;
animation: pulseSub 1.8s infinite alternate;
}
@keyframes pulseSub {
from { opacity: 0.4; color: #666; }
to { opacity: 1; color: #ffffff; text-shadow: var(--glow-subtle); }
}
/* ── Desktop Right-Click Context Menu ────────────────────────────────────── */
.context-menu {
position: fixed;
z-index: 9999;
background: rgba(10, 10, 12, 0.96);
border: 1px solid var(--border-active);
box-shadow: 0 10px 30px rgba(0,0,0,0.8);
backdrop-filter: blur(20px);
padding: 4px 0;
min-width: 180px;
display: none;
}
.ctx-item {
padding: 7px 14px;
font-size: 11px;
color: #ddd;
cursor: pointer;
display: flex;
align-items: center;
gap: 8px;
transition: background 0.1s ease;
}
.ctx-item:hover {
background: rgba(255, 255, 255, 0.12);
color: #ffffff;
text-shadow: var(--glow-subtle);
}
.ctx-item i {
width: 14px;
font-size: 11px;
}
.ctx-sep {
height: 1px;
background: rgba(255, 255, 255, 0.1);
margin: 4px 0;
}
/* ── Responsive Window Bounds ────────────────────────────────────────────── */
@media (max-width: 900px) {
.window {
position: static !important;
width: 100% !important;
height: auto !important;
margin-bottom: 12px;
}
.desktop {
overflow-y: auto;
padding: 12px;
height: calc(100vh - var(--taskbar-h));
}
.taskbar-windows {
display: none;
}
.music-track {
display: none;
}
}
</style>
</head>
<body>
<!-- Minimal Boot Screen (Click to Start Audio & Video) -->
<div id="loadingScreen" onclick="enterKatOS()">
<div class="boot-minimal">
<div class="boot-logo">katOS</div>
<div class="boot-sub">[ click to enter ]</div>
</div>
</div>
<!-- Dynamic Ambient & Cybernetic Background Layers -->
<video class="video-bg" id="bgVideo" autoplay muted loop playsinline></video>
<canvas id="bgCanvas"></canvas>
<div class="crt-overlay"></div>
<div id="katToast" class="kat-toast"></div>
<!-- Desktop Workspace -->
<div class="desktop" id="desktop">
<!-- About Window -->
<div class="window active" id="win-about">
<div class="titlebar">
<div class="title-left">
<i class="fas fa-file-lines"></i>
<span>about.md</span>
</div>
<div class="window-controls">
<div class="win-dot-close" onclick="closeWindow('win-about')" title="Close"></div>
</div>
</div>
<div class="window-body">
<p style="color:#ffffff; font-weight:500; margin-bottom: 12px;">i cant stop loving linux, pls speed i need this</p>
<p style="color:#aaa; margin-bottom: 12px;">minecraft shyt, networking experiments, and linux customization.</p>
<p style="color:#555; margin-bottom: 12px; font-family:monospace;">i was on the ███████████████</p>
<p style="color:#fff; text-shadow:var(--glow-subtle); margin-bottom: 12px;">cats &gt; people haha.</p>
<p style="color:#bbb; margin-bottom: 12px;">i use arch yea</p>
<p style="color:#ffffff; font-weight:600; text-shadow:var(--glow-subtle);">you da real kat 🤍.</p>
</div>
</div>
<!-- Skills Window -->
<div class="window" id="win-skills">
<div class="titlebar">
<div class="title-left">
<i class="fas fa-terminal"></i>
<span>skillz.bash</span>
</div>
<div class="window-controls">
<div class="win-dot-close" onclick="closeWindow('win-skills')" title="Close"></div>
</div>
</div>
<div class="window-body">
<div style="margin-bottom: 8px; color: var(--text-dim);"># system competencies:</div>
<span class="skill-tag"><i class="fab fa-linux"></i> linux / arch</span>
<span class="skill-tag"><i class="fas fa-cube"></i> minecraft servers</span>
<span class="skill-tag"><i class="fas fa-server"></i> self hosting</span>
<span class="skill-tag"><i class="fas fa-network-wired"></i> server networking</span>
<span class="skill-tag"><i class="fab fa-java"></i> java / plugins</span>
<div style="margin: 14px 0 6px; color: var(--text-dim);"># personality traits:</div>
<span class="skill-tag">yappin</span>
<span class="skill-tag">im crine</span>
<span class="skill-tag">tweaking</span>
<span class="skill-tag">hahahahahha</span>
<span class="skill-tag">cool content</span>
<span class="skill-tag">very funny</span>
<span class="skill-tag"><i class="fas fa-cat"></i> veri fond of cats</span>
</div>
</div>
<!-- Websites & Projects Window -->
<div class="window" id="win-websites">
<div class="titlebar">
<div class="title-left">
<i class="fas fa-globe"></i>
<span>websites.html</span>
</div>
<div class="window-controls">
<div class="win-dot-close" onclick="closeWindow('win-websites')" title="Close"></div>
</div>
</div>
<div class="window-body">
<a class="link-item" href="https://katdrop.oiupoyt.space" target="_blank" rel="noopener">
<i class="fas fa-file-arrow-up"></i>
<div>
<div style="color:#fff; font-weight:500;">katdrop.oiupoyt.space</div>
<div style="font-size:10px; color:#777;">minimal 10-min file transfer</div>
</div>
<span class="badge">live</span>
</a>
<a class="link-item" href="https://kats.oiupoyt.space" target="_blank" rel="noopener">
<i class="fas fa-cat"></i>
<div>
<div style="color:#fff; font-weight:500;">kats.oiupoyt.space</div>
<div style="font-size:10px; color:#777;">look at cat. feel good.</div>
</div>
<span class="badge">live</span>
</a>
<a class="link-item" href="https://jd.oiupoyt.space" target="_blank" rel="noopener">
<i class="fas fa-download"></i>
<div>
<div style="color:#fff; font-weight:500;">jd.oiupoyt.space</div>
<div style="font-size:10px; color:#777;">just download yt &amp; ig media</div>
</div>
<span class="badge">live</span>
</a>
<a class="link-item" href="https://toolbox.oiupoyt.space" target="_blank" rel="noopener">
<i class="fas fa-wrench"></i>
<div>
<div style="color:#fff; font-weight:500;">toolbox.oiupoyt.space</div>
<div style="font-size:10px; color:#777;">client-side dev utility suite</div>
</div>
<span class="badge">live</span>
</a>
</div>
</div>
<!-- Social Links Window -->
<div class="window" id="win-links">
<div class="titlebar">
<div class="title-left">
<i class="fas fa-folder-closed"></i>
<span>links.zip</span>
</div>
<div class="window-controls">
<div class="win-dot-close" onclick="closeWindow('win-links')" title="Close"></div>
</div>
</div>
<div class="window-body">
<a class="link-item" href="https://github.com/oiupoyt" target="_blank" rel="noopener">
<i class="fab fa-github"></i>
<span>GitHub</span>
<span class="badge">@oiupoyt</span>
</a>
<a class="link-item" href="https://discordapp.com/users/1082968457559146506" target="_blank" rel="noopener">
<i class="fab fa-discord"></i>
<span>Discord</span>
<span class="badge">@oiupoyt</span>
</a>
<a class="link-item" href="https://www.instagram.com/oiupoyt/" target="_blank" rel="noopener">
<i class="fab fa-instagram"></i>
<span>Instagram</span>
<span class="badge">@oiupoyt</span>
</a>
<a class="link-item" href="https://www.youtube.com/@OIUPOYT" target="_blank" rel="noopener">
<i class="fab fa-youtube"></i>
<span>YouTube</span>
<span class="badge">@OIUPOYT</span>
</a>
<a class="link-item" href="https://www.roblox.com/users/1529210346/profile" target="_blank" rel="noopener">
<i class="fas fa-gamepad"></i>
<span>Roblox</span>
<span class="badge">oiupoyt</span>
</a>
<a class="link-item" href="https://modrinth.com/user/oiupoyt" target="_blank" rel="noopener">
<i class="fas fa-cube"></i>
<span>Modrinth</span>
<span class="badge">mods</span>
</a>
</div>
</div>
<!-- Interactive FastFetch Terminal Window -->
<div class="window active" id="win-term">
<div class="titlebar">
<div class="title-left">
<i class="fas fa-terminal"></i>
<span>~fastfetch</span>
</div>
<div class="window-controls">
<div class="win-dot-close" onclick="closeWindow('win-term')" title="Close"></div>
</div>
</div>
<div class="terminal-body" id="termBody" onclick="focusTermInput()">
<div class="fastfetch-pre" id="fastfetchArt"></div>
<div id="termHistory"></div>
<div class="term-prompt-line">
<span class="term-prompt">oiupoyt@archlinux:~$</span>
<input type="text" class="term-input" id="termInput" autocomplete="off" spellcheck="false" autofocus>
</div>
</div>
</div>
<!-- Audio Visualizer Window -->
<div class="window" id="win-viz">
<div class="titlebar">
<div class="title-left">
<i class="fas fa-wave-square"></i>
<span>visualizer</span>
</div>
<div class="window-controls">
<div class="win-dot-close" onclick="closeWindow('win-viz')" title="Close"></div>
</div>
</div>
<div class="viz-container" id="vizBox">
<canvas id="vizCanvas" title="Click to cycle visualizer mode"></canvas>
<div class="viz-overlay">
<span>mode:</span>
<span class="viz-mode-badge" id="vizModeName">spectrum bars</span>
</div>
<div class="viz-hint">[click visualizer to switch mode]</div>
<span class="viz-idle-msg" id="vizIdleMsg">play music to visualize</span>
</div>
</div>
</div>
<!-- Taskbar -->
<footer class="taskbar">
<div class="taskbar-left">
<div class="os-logo-btn" onclick="restoreAllWindows()" title="katOS Launcher">
<span>katOS</span>
</div>
<div class="taskbar-divider"></div>
<div class="taskbar-windows" id="taskbarWinContainer"></div>
</div>
<div class="taskbar-right">
<!-- Music Player Controls -->
<div class="music-bar">
<i class="fas fa-compact-disc music-icon paused" id="musicDisc"></i>
<span class="music-track" id="musicTrack">Baiaa081 - Noite Isolada</span>
<button class="player-btn" id="playBtn" onclick="togglePlay()" title="Play/Pause">
<i class="fas fa-play" id="playIcon"></i>
</button>
<input type="range" class="volume-slider" id="volSlider" min="0" max="100" value="45" title="Volume">
<button class="player-btn" onclick="cycleVizMode()" title="Switch Visualizer Mode">
<i class="fas fa-sliders"></i>
</button>
</div>
<!-- System Tray Clock (RAM removed) -->
<span class="tray-time" id="clock">00:00:00</span>
</div>
</footer>
<!-- Desktop Right-Click Context Menu -->
<div class="context-menu" id="ctxMenu">
<div class="ctx-item" onclick="spawnFastfetch()"><i class="fas fa-terminal"></i> Open Terminal</div>
<div class="ctx-item" onclick="cycleBgMode()"><i class="fas fa-layer-group"></i> Switch Background Mode</div>
<div class="ctx-sep"></div>
<div class="ctx-item" onclick="togglePlay()"><i class="fas fa-music"></i> Play / Pause Music</div>
<div class="ctx-item" onclick="restoreAllWindows()"><i class="fas fa-window-restore"></i> Show All Windows</div>
<div class="ctx-item" onclick="autoSpaceWindows()"><i class="fas fa-rotate-left"></i> Reset &amp; Space Windows</div>
</div>
<!-- Audio Element -->
<audio id="bgMusic" loop preload="auto">
<source src="music.mp3" type="audio/mpeg">
</audio>
<script>
// ── Global System State ──────────────────────────────────────────────────
let topZIndex = 40;
const windowRegistry = ['win-about', 'win-skills', 'win-websites', 'win-links', 'win-term', 'win-viz'];
// ── Auto-Space Terminals on Start (Organic Staggered Layout) ───────────
function autoSpaceWindows() {
const W = window.innerWidth;
const H = window.innerHeight - 36; // leave room for taskbar
if (W >= 1500) {
// Broad monitor: snug fit without dead space, content-fitted heights
setWinPos('win-about', 26, 32, 360, null);
setWinPos('win-skills', 324, 44, 365, null);
setWinPos('win-websites', 38, 424, 355, null);
setWinPos('win-links', 346, 440, 340, null);
setWinPos('win-term', 24, 805, Math.min(650, W - 835), 378);
setWinPos('win-viz', 422, 805, Math.min(650, W - 835), 245);
} else if (W >= 1150) {
// Standard laptop / desktop: non-parallel coordinates & no overlap
setWinPos('win-about', 18, 20, 340, null);
setWinPos('win-skills', 314, 30, 350, null);
setWinPos('win-websites', 28, 382, 345, null);
setWinPos('win-links', 334, 400, 335, null);
setWinPos('win-term', 18, 748, Math.max(580, W - 770), 378);
setWinPos('win-viz', 416, 748, Math.max(580, W - 770), 240);
} else if (W >= 800) {
// Tablet / compact
setWinPos('win-about', 16, 16, 340, null);
setWinPos('win-skills', 310, 25, 345, null);
setWinPos('win-websites', 24, 380, 335, null);
setWinPos('win-links', 330, 395, 325, null);
setWinPos('win-term', 650, 20, W - 44, 378);
setWinPos('win-viz', 1048, 20, W - 44, 240);
}
restoreAllWindows();
bringToFront(document.getElementById('win-term'));
}
function setWinPos(id, top, left, width, height) {
const win = document.getElementById(id);
if (!win) return;
win.style.top = top + 'px';
win.style.left = left + 'px';
if (width) win.style.width = width + 'px';
if (height) {
win.style.height = height + 'px';
} else {
win.style.height = 'auto';
}
}
// ── Window Dragging & State ──────────────────────────────────────────────
function bringToFront(win) {
if (!win) return;
topZIndex++;
win.style.zIndex = topZIndex;
document.querySelectorAll('.window').forEach(w => w.classList.remove('active'));
win.classList.add('active');
updateTaskbar();
}
function initWindows() {
document.querySelectorAll('.window').forEach(win => {
const bar = win.querySelector('.titlebar');
if (!bar) return;
// Focus on click
win.addEventListener('mousedown', () => bringToFront(win));
// Dragging
bar.addEventListener('mousedown', e => {
if (e.target.closest('.win-dot-close')) return;
bringToFront(win);
if (win.classList.contains('maximized')) return;
const rect = win.getBoundingClientRect();
const shiftX = e.clientX - rect.left;
const shiftY = e.clientY - rect.top;
function onMouseMove(moveEvent) {
let newX = moveEvent.clientX - shiftX;
let newY = moveEvent.clientY - shiftY;
const maxX = window.innerWidth - 60;
const maxY = window.innerHeight - 70;
if (newX < 0) newX = 0;
if (newX > maxX) newX = maxX;
if (newY < 0) newY = 0;
if (newY > maxY) newY = maxY;
win.style.left = newX + 'px';
win.style.top = newY + 'px';
}
function onMouseUp() {
document.removeEventListener('mousemove', onMouseMove);
document.removeEventListener('mouseup', onMouseUp);
}
document.addEventListener('mousemove', onMouseMove);
document.addEventListener('mouseup', onMouseUp);
});
});
autoSpaceWindows();
updateTaskbar();
}
function closeWindow(id) {
const win = document.getElementById(id);
if (!win) return;
win.classList.add('minimized');
updateTaskbar();
}
function restoreWindow(id) {
const win = document.getElementById(id);
if (!win) return;
if (win.classList.contains('minimized')) {
win.classList.remove('minimized');
bringToFront(win);
} else if (win.classList.contains('active')) {
win.classList.add('minimized');
} else {
bringToFront(win);
}
updateTaskbar();
}
function restoreAllWindows() {
windowRegistry.forEach(id => {
const win = document.getElementById(id);
if (win) {
win.classList.remove('minimized');
win.classList.remove('maximized');
}
});
updateTaskbar();
}
function updateTaskbar() {
const container = document.getElementById('taskbarWinContainer');
if (!container) return;
container.innerHTML = '';
windowRegistry.forEach(id => {
const win = document.getElementById(id);
if (!win) return;
const titleEl = win.querySelector('.title-left span');
const iconEl = win.querySelector('.title-left i');
const titleText = titleEl ? titleEl.textContent : id;
const iconClass = iconEl ? iconEl.className : 'fas fa-terminal';
const btn = document.createElement('div');
btn.className = 'taskbar-win-btn' + (win.classList.contains('active') && !win.classList.contains('minimized') ? ' active' : '');
btn.innerHTML = `<i class="${iconClass}"></i><span>${titleText}</span>`;
btn.onclick = () => restoreWindow(id);
container.appendChild(btn);
});
}
// ── FastFetch ASCII Art & Specs (Monochrome Glow) ─────────────────────────
const fastfetchArtEl = document.getElementById('fastfetchArt');
const fa = cls => `<i class="${cls}" style="width:1.2em;text-align:center;display:inline-block;opacity:0.75;font-size:10px;"></i>`;
const fastfetchLines = [
` ▄`,
` ▟█▙`,
` ▟███▙ <b style="color:#ffffff; text-shadow:var(--glow-subtle);">oiupoyt</b><span style="opacity:0.4">@</span><b style="color:#ffffff;">archlinux</b>`,
` ▟█████▙ <span style="opacity:0.35">────────────────────────</span>`,
` ▟███████▙ ${fa('fas fa-laptop')} <span style="opacity:0.45;font-size:10px">OS</span> Arch Linux x86_64`,
` ▂▔▀▜██████▙ ${fa('fas fa-microchip')} <span style="opacity:0.45;font-size:10px">Kernel</span> Linux 6.19.8-arch1-1`,
` ▟██▅▂▝▜█████▙ ${fa('fas fa-box-open')} <span style="opacity:0.45;font-size:10px">Packages</span> 1254 (pacman), 60 (flatpak)`,
` ▟█████████████▙ ${fa('fas fa-terminal')} <span style="opacity:0.45;font-size:10px">Shell</span> fish 4.5.0`,
` ▟███████████████▙ ${fa('fas fa-display')} <span style="opacity:0.45;font-size:10px">Terminal</span> kitty 0.46.1`,
` ▟█████████████████▙ ${fa('fas fa-window-restore')} <span style="opacity:0.45;font-size:10px">WM</span> Hyprland 0.54.2 (Wayland)`,
` ▟███████████████████▙ ${fa('fas fa-palette')} <span style="opacity:0.45;font-size:10px">Theme</span> monochrome-dark`,
` ▟█████████▛▀▀▜████████▙ ${fa('fas fa-font')} <span style="opacity:0.45;font-size:10px">Font</span> Space Mono (11pt)`,
` ▟████████▛ ▜███████▙ ${fa('fas fa-clock')} <span style="opacity:0.45;font-size:10px">Uptime</span> <span id="dynUptime">12 hours, 42 mins</span>`,
` ▟█████████ ████████▙ ${fa('fas fa-music')} <span style="opacity:0.45;font-size:10px">Music</span> Baiaa081 - Noite Isolada`,
` ▟██████████ █████▆▅▄▃▂ ${fa('fas fa-cat')} <span style="opacity:0.45;font-size:10px">Status</span> you da real kat 🤍`,
` ▟██████████▛ ▜█████████▙ ${fa('fas fa-network-wired')} <span style="opacity:0.45;font-size:10px">Websites</span> katdrop • kats • jd • toolbox`,
` ▟██████▀▀▀ ▀▀██████▙`,
` ▟███▀▘ ▝▀███▙`,
` ▟▛▀ ▀▜▙`,
];
fastfetchArtEl.innerHTML = fastfetchLines.map(l => `<div class="term-row">${l}</div>`).join('');
// ── Interactive Terminal Shell ───────────────────────────────────────────
const termInput = document.getElementById('termInput');
const termHistory = document.getElementById('termHistory');
const termBody = document.getElementById('termBody');
let cmdHistory = [];
let historyIdx = -1;
function focusTermInput() {
termInput.focus();
}
termInput.addEventListener('keydown', e => {
if (e.key === 'Enter') {
const cmd = termInput.value.trim();
if (cmd) {
cmdHistory.push(cmd);
historyIdx = cmdHistory.length;
execCommand(cmd);
}
termInput.value = '';
termBody.scrollTop = termBody.scrollHeight;
} else if (e.key === 'ArrowUp') {
if (historyIdx > 0) {
historyIdx--;
termInput.value = cmdHistory[historyIdx];
}
} else if (e.key === 'ArrowDown') {
if (historyIdx < cmdHistory.length - 1) {
historyIdx++;
termInput.value = cmdHistory[historyIdx];
} else {
historyIdx = cmdHistory.length;
termInput.value = '';
}
} else if (e.key === 'Tab') {
e.preventDefault();
const cur = termInput.value.trim().toLowerCase();
const available = ['help', 'fastfetch', 'neofetch', 'clear', 'cat', 'ls', 'projects', 'skills', 'music', 'matrix', 'whoami', 'date', 'reboot'];
const match = available.find(c => c.startsWith(cur));
if (match) termInput.value = match;
}
});
function execCommand(cmdLine) {
const parts = cmdLine.split(' ');
const cmd = parts[0].toLowerCase();
const arg = parts.slice(1).join(' ');
const row = document.createElement('div');
row.innerHTML = `<span class="term-prompt">oiupoyt@archlinux:~$</span> <span class="term-cmd">${escapeHtml(cmdLine)}</span>`;
termHistory.appendChild(row);
const out = document.createElement('div');
out.className = 'term-output';
switch(cmd) {
case 'help':
out.innerHTML = `Available commands:
<b style="color:#ffffff">fastfetch</b> - display Arch Linux logo & system specs
<b style="color:#ffffff">ls</b> - list virtual directory contents
<b style="color:#ffffff">cat &lt;file&gt;</b> - display contents of a file (e.g. cat about.md)
<b style="color:#ffffff">projects</b> - list active web projects & links
<b style="color:#ffffff">skills</b> - show competencies & skills
<b style="color:#ffffff">music</b> - music play | pause | vol &lt;0-100&gt;
<b style="color:#ffffff">bg &lt;mode&gt;</b> - change background (liquid, horizon, matrix, video)
<b style="color:#ffffff">catpic</b> - fetch a fresh cat image
<b style="color:#ffffff">whoami</b> - print current session bio
<b style="color:#ffffff">clear</b> - clear terminal screen
<b style="color:#ffffff">date</b> - show current date and time
<b style="color:#ffffff">reboot</b> - restart katOS desktop`;
break;
case 'fastfetch':
case 'neofetch':
fastfetchArtEl.style.display = 'block';
out.textContent = '';
break;
case 'ls':
out.innerHTML = `<span style="color:#ffffff">about.md</span> <span style="color:#ffffff">skillz.bash</span> <span style="color:#ffffff">websites.html</span> <span style="color:#ffffff">links.zip</span> <span style="color:#888">music.mp3</span> <span style="color:#888">backgroundVideo.mp4</span>`;
break;
case 'cat':
if (!arg) {
out.textContent = 'cat: missing file operand. try: cat about.md';
} else if (arg.includes('about')) {
out.textContent = `i cant stop loving linux, pls speed i need this\nminecraft shyt, networking experiments, and linux customization.\ni was on the ███████████████\ncats > people haha.\ni use arch yea\nyou da real kat 🤍.`;
} else if (arg.includes('skill')) {
out.textContent = `linux / arch • Minecraft Servers • Self Hosting • server networking • Java / Plugins • yappin • im crine • tweaking • hahahahahha • cool content • very funny • veri fond of cats`;
} else if (arg.includes('website')) {
out.innerHTML = `• katdrop: https://katdrop.oiupoyt.space\n• kats: https://kats.oiupoyt.space\n• jd: https://jd.oiupoyt.space\n• toolbox: https://toolbox.oiupoyt.space`;
} else if (arg.includes('link')) {
out.innerHTML = `• GitHub: https://github.com/oiupoyt\n• Discord: @oiupoyt\n• Instagram: @oiupoyt\n• YouTube: @OIUPOYT\n• Modrinth: oiupoyt\n• Roblox: oiupoyt`;
} else {
out.textContent = `cat: ${arg}: Permission denied or file is binary`;
}
break;
case 'projects':
out.innerHTML = `oiupoyt projects:
1. <a href="https://katdrop.oiupoyt.space" target="_blank" style="color:#ffffff; text-decoration:underline;">katdrop.oiupoyt.space</a> - minimal 10-min file transfer service
2. <a href="https://kats.oiupoyt.space" target="_blank" style="color:#ffffff; text-decoration:underline;">kats.oiupoyt.space</a> - look at cat. feel good.
3. <a href="https://jd.oiupoyt.space" target="_blank" style="color:#ffffff; text-decoration:underline;">jd.oiupoyt.space</a> - youtube & instagram media downloader
4. <a href="https://toolbox.oiupoyt.space" target="_blank" style="color:#ffffff; text-decoration:underline;">toolbox.oiupoyt.space</a> - client-side developer utility suite`;
break;
case 'skills':
out.textContent = 'linux / arch, minecraft servers, self hosting, server networking, java / plugins, yappin, im crine, tweaking, cats';
break;
case 'music':
if (arg === 'play') {
playMusic();
out.textContent = 'audio: playing';
} else if (arg === 'pause') {
pauseMusic();
out.textContent = 'audio: paused';
} else if (arg.startsWith('vol')) {
const val = parseInt(arg.split(' ')[1]);
if (!isNaN(val) && val >= 0 && val <= 100) {
setVolume(val);
out.textContent = `audio: volume set to ${val}%`;
} else {
out.textContent = 'usage: music vol <0-100>';
}
} else {
out.textContent = 'usage: music [play | pause | vol <0-100>]';
}
break;
case 'catpic':
case 'cat-image':
case 'cat':
out.innerHTML = `Fetching cat...`;
fetch('https://api.thecatapi.com/v1/images/search')
.then(r => r.json())
.then(data => {
if (data && data[0] && data[0].url) {
out.innerHTML = `<img src="${data[0].url}" style="max-width:280px;max-height:200px;border:1px solid #444;margin-top:6px;display:block;"><span style="font-size:10px;color:#888;">meow</span>`;
termBody.scrollTop = termBody.scrollHeight;
}
}).catch(() => {
out.textContent = 'cat error: no cat found :(';
});
break;
case 'bg':
case 'background':
if (arg === 'liquid' || arg === 'void' || arg === 'fluid') {
setBgMode('liquid-void');
out.textContent = 'background set to: Liquid Void Fluid';
} else if (arg === 'cyber' || arg === 'horizon') {
setBgMode('cyber-horizon');
out.textContent = 'background set to: Cyber 3D Horizon';
} else if (arg === 'matrix' || arg === 'rain') {
setBgMode('matrix-rain');
out.textContent = 'background set to: KatOS Digital Rain';
} else if (arg === 'video') {
setBgMode('video-ambient');
out.textContent = 'background set to: Ambient Video';
} else {
cycleBgMode();
out.textContent = `background switched to: ${bgModes[currentBgModeIdx].name}\nusage: bg [liquid | horizon | matrix | video]`;
}
break;
case 'matrix':
setBgMode('matrix-rain');
out.textContent = 'background set to: KatOS Digital Rain';
break;
case 'clear':
termHistory.innerHTML = '';
fastfetchArtEl.style.display = 'none';
return;
case 'whoami':
out.textContent = 'oiupoyt — linux enthusiast, minecraft tinkerer, cat lover';
break;
case 'uname':
case 'uname -a':
out.textContent = 'Linux katOS 6.19.8-arch1-1 #1 SMP PREEMPT_DYNAMIC x86_64 GNU/Linux';
break;
case 'date':
out.textContent = new Date().toString();
break;
case 'reboot':
location.reload();
return;
default:
out.textContent = `bash: ${cmd}: command not found. type 'help' for available commands.`;
}
termHistory.appendChild(out);
termBody.scrollTop = termBody.scrollHeight;
}
function spawnFastfetch() {
restoreWindow('win-term');
fastfetchArtEl.style.display = 'block';
}
function escapeHtml(str) {
return String(str).replace(/[&<>'"]/g, t => ({
'&': '&amp;', '<': '&lt;', '>': '&gt;', "'": '&#39;', '"': '&quot;'
}[t] || t));
}
// ── Audio & Visualizer Engine (Glow White Monochrome) ─────────────────────
const bgMusic = document.getElementById('bgMusic');
const playBtn = document.getElementById('playBtn');
const playIcon = document.getElementById('playIcon');
const musicDisc = document.getElementById('musicDisc');
const volSlider = document.getElementById('volSlider');
const vizCanvas = document.getElementById('vizCanvas');
const vizCtx = vizCanvas.getContext('2d');
const vizIdleMsg = document.getElementById('vizIdleMsg');
const vizModeName = document.getElementById('vizModeName');
let isPlaying = false;
let audioCtx, analyser, source, vizAnimId;
let vizConnected = false;
const vizModes = ['spectrum bars', 'mirror pulse'];
let currentVizModeIdx = 0;
function setupAudio() {
if (vizConnected) return;
try {
audioCtx = new (window.AudioContext || window.webkitAudioContext)();
analyser = audioCtx.createAnalyser();
analyser.fftSize = 256;
analyser.smoothingTimeConstant = 0.82;
source = audioCtx.createMediaElementSource(bgMusic);
source.connect(analyser);
analyser.connect(audioCtx.destination);
vizConnected = true;
} catch (err) {
console.warn('AudioContext init error:', err);
}
}
function playMusic() {
setupAudio();
if (audioCtx && audioCtx.state === 'suspended') {
audioCtx.resume();
}
bgMusic.play().then(() => {
isPlaying = true;
playIcon.className = 'fas fa-pause';
musicDisc.classList.remove('paused');
startVisualizer();
}).catch(err => {
console.warn('Audio play prevented:', err);
});
}
function pauseMusic() {
bgMusic.pause();
isPlaying = false;
playIcon.className = 'fas fa-play';
musicDisc.classList.add('paused');
stopVisualizer();
}
function togglePlay() {
if (isPlaying) {
pauseMusic();
} else {
playMusic();
}
}
function setVolume(val) {
bgMusic.volume = val / 100;
volSlider.value = val;
}
volSlider.addEventListener('input', e => {
setVolume(e.target.value);
});
bgMusic.volume = volSlider.value / 100;
bgMusic.addEventListener('ended', () => {
pauseMusic();
});
// ── Visualizer Rendering (Glow White Spectrum) ────────────────────────────
function cycleVizMode() {
currentVizModeIdx = (currentVizModeIdx + 1) % vizModes.length;
vizModeName.textContent = vizModes[currentVizModeIdx];
}
vizCanvas.addEventListener('click', cycleVizMode);
function resizeVizCanvas() {
const rect = vizCanvas.getBoundingClientRect();
const dpr = window.devicePixelRatio || 1;
vizCanvas.width = rect.width * dpr;
vizCanvas.height = rect.height * dpr;
}
function startVisualizer() {
if (!analyser) return;
vizIdleMsg.style.opacity = '0';
cancelAnimationFrame(vizAnimId);
drawVisualizerFrame();
}
function stopVisualizer() {
cancelAnimationFrame(vizAnimId);
resizeVizCanvas();
const W = vizCanvas.width;
const H = vizCanvas.height;
vizCtx.clearRect(0, 0, W, H);
// Baseline resting line
vizCtx.strokeStyle = 'rgba(255, 255, 255, 0.1)';
vizCtx.lineWidth = 1;
vizCtx.beginPath();
vizCtx.moveTo(0, H * 0.92);
vizCtx.lineTo(W, H * 0.92);
vizCtx.stroke();
vizIdleMsg.style.opacity = '1';
}
function drawVisualizerFrame() {
vizAnimId = requestAnimationFrame(drawVisualizerFrame);
resizeVizCanvas();
const W = vizCanvas.width;
const H = vizCanvas.height;
if (W === 0 || H === 0) return;
vizCtx.clearRect(0, 0, W, H);
const bufLen = analyser.frequencyBinCount;
const freqData = new Uint8Array(bufLen);
analyser.getByteFrequencyData(freqData);
const mode = vizModes[currentVizModeIdx];
if (mode === 'spectrum bars') {
// Mode 1: Squared Glowing White Spectrum Bars
const barCount = 26;
const gap = W * 0.012;
const barW = (W - gap * (barCount + 1)) / barCount;
const step = Math.floor(bufLen / barCount);
const maxH = H * 0.82;
const baseY = H * 0.92;
for (let i = 0; i < barCount; i++) {
let sum = 0;
for (let k = 0; k < step; k++) sum += freqData[i * step + k];
const ratio = (sum / step) / 255;
const bh = ratio * maxH;
const x = gap + i * (barW + gap);
const y = baseY - bh;
const alpha = 0.25 + ratio * 0.75;
const grad = vizCtx.createLinearGradient(0, y, 0, baseY);
grad.addColorStop(0, `rgba(255, 255, 255, ${alpha})`);
grad.addColorStop(0.5, `rgba(220, 220, 220, ${alpha * 0.6})`);
grad.addColorStop(1, `rgba(180, 180, 180, ${alpha * 0.1})`);
vizCtx.fillStyle = grad;
vizCtx.fillRect(x, y, barW, bh);
// Glowing white peak cap
if (bh > 4) {
vizCtx.fillStyle = `rgba(255, 255, 255, ${Math.min(1, alpha + 0.3)})`;
vizCtx.shadowColor = 'rgba(255, 255, 255, 0.8)';
vizCtx.shadowBlur = 6;
vizCtx.fillRect(x, y - 3, barW, 2);
vizCtx.shadowBlur = 0;
}
}
// Base line
vizCtx.strokeStyle = 'rgba(255, 255, 255, 0.12)';
vizCtx.lineWidth = 1;
vizCtx.beginPath();
vizCtx.moveTo(0, baseY);
vizCtx.lineTo(W, baseY);
vizCtx.stroke();
} else if (mode === 'mirror pulse') {
// Mode 2: Symmetric Center-Out Pulse
const barCount = 18;
const center = W / 2;
const barW = (W / 2) / (barCount + 1);
const maxH = H * 0.75;
const centerY = H / 2;
for (let i = 0; i < barCount; i++) {
const ratio = freqData[i * 4] / 255;
const bh = ratio * maxH;
const alpha = 0.2 + ratio * 0.8;
vizCtx.fillStyle = `rgba(255, 255, 255, ${alpha})`;
vizCtx.fillRect(center + i * barW, centerY - bh / 2, barW - 2, bh);
vizCtx.fillRect(center - (i + 1) * barW, centerY - bh / 2, barW - 2, bh);
}
vizCtx.strokeStyle = 'rgba(255, 255, 255, 0.2)';
vizCtx.lineWidth = 1;
vizCtx.beginPath();
vizCtx.moveTo(0, centerY);
vizCtx.lineTo(W, centerY);
vizCtx.stroke();
}
}
// ── Unique Cybernetic & Audio-Reactive Background Engine ────────────────
const bgCanvas = document.getElementById('bgCanvas');
const bgCtx = bgCanvas.getContext('2d');
const bgVideo = document.getElementById('bgVideo');
const katToast = document.getElementById('katToast');
let toastTimer = null;
function showToast(text) {
if (!katToast) return;
katToast.textContent = text;
katToast.classList.add('show');
clearTimeout(toastTimer);
toastTimer = setTimeout(() => {
katToast.classList.remove('show');
}, 1900);
}
const bgModes = [
{ id: 'liquid-void', name: 'Liquid Void Fluid' },
{ id: 'cyber-horizon', name: 'Cyber 3D Horizon' },
{ id: 'matrix-rain', name: 'KatOS Digital Rain' },
{ id: 'video-ambient', name: 'Ambient Video' }
];
let currentBgModeIdx = 0; // Default: 'liquid-void'
const savedBg = localStorage.getItem('katos_bg_mode');
if (savedBg && savedBg !== 'video-ambient') {
const idx = bgModes.findIndex(m => m.id === savedBg);
if (idx !== -1) currentBgModeIdx = idx;
} else {
currentBgModeIdx = 0;
localStorage.setItem('katos_bg_mode', 'liquid-void');
}
function setBgMode(modeId, silent = false) {
const idx = bgModes.findIndex(m => m.id === modeId);
if (idx !== -1) {
currentBgModeIdx = idx;
applyBgMode(silent);
}
}
function cycleBgMode() {
currentBgModeIdx = (currentBgModeIdx + 1) % bgModes.length;
applyBgMode(false);
}
function applyBgMode(silent = false) {
const mode = bgModes[currentBgModeIdx];
localStorage.setItem('katos_bg_mode', mode.id);
if (mode.id === 'video-ambient') {
bgVideo.style.display = 'block';
tryLoadBgVideo();
setTimeout(() => { bgVideo.style.opacity = '0.55'; }, 20);
} else {
bgVideo.style.opacity = '0';
try { bgVideo.pause(); } catch(e) {}
setTimeout(() => {
if (bgModes[currentBgModeIdx].id !== 'video-ambient') bgVideo.style.display = 'none';
}, 300);
}
if (!silent) showToast(`[ BG: ${mode.name} ]`);
}
// Interactive Mouse Tracking (Position & Kinetic Velocity)
const bgMouse = {
x: window.innerWidth / 2,
y: window.innerHeight / 2,
targetX: window.innerWidth / 2,
targetY: window.innerHeight / 2,
vx: 0,
vy: 0,
active: false
};
window.addEventListener('mousemove', e => {
bgMouse.targetX = e.clientX;
bgMouse.targetY = e.clientY;
bgMouse.active = true;
});
window.addEventListener('mouseleave', () => {
bgMouse.active = false;
});
// Gravitational Shockwaves on Click
const shockwaves = [];
window.addEventListener('mousedown', e => {
if (e.target.closest('.window-body') || e.target.closest('input') || e.target.closest('.player-btn')) return;
shockwaves.push({
x: e.clientX,
y: e.clientY,
radius: 10,
maxRadius: Math.max(window.innerWidth, window.innerHeight) * 0.45,
alpha: 0.65,
power: 1.2
});
});
// ── Liquid Void Kinetic Particle Fluid ──────────────────────────────────
const FLUID_PARTICLE_COUNT = 380;
const fluidParticles = [];
function initFluidParticles() {
fluidParticles.length = 0;
const W = bgCanvas.width;
const H = bgCanvas.height;
for (let i = 0; i < FLUID_PARTICLE_COUNT; i++) {
const x = Math.random() * W;
const y = Math.random() * H;
fluidParticles.push({
x: x,
y: y,
oldX: x,
oldY: y,
vx: (Math.random() - 0.5) * 0.5,
vy: (Math.random() - 0.5) * 0.5,
speed: 0.85 + Math.random() * 1.3,
size: 0.7 + Math.random() * 1.7,
alpha: 0.22 + Math.random() * 0.65,
life: Math.random() * 500,
maxLife: 300 + Math.random() * 600
});
}
}
// Incompressible Curl-Noise Flow Field Generator (Zero Divergence)
function getCurlVector(x, y, t) {
const s1 = 0.0016;
const s2 = 0.0034;
const vx = -s1 * Math.sin(x * s1 + t * 0.00035) * Math.sin(y * s1 + t * 0.0003) + 0.45 * s2 * Math.cos((x + y) * s2 - t * 0.00045);
const vy = -(s1 * Math.cos(x * s1 + t * 0.00035) * Math.cos(y * s1 + t * 0.0003) + 0.45 * s2 * Math.cos((x + y) * s2 - t * 0.00045));
const len = Math.hypot(vx, vy) || 1;
return { vx: vx / len, vy: vy / len };
}
// Matrix Rain Setup
let mCols = 0;
let mDrops = [];
const mChars = '01KATOSOIUPOYTARCHLINUX7942#$><;:~^._.^';
function initMatrixDrops() {
mCols = Math.floor(bgCanvas.width / 18);
mDrops = Array.from({ length: mCols }, () => Math.floor(Math.random() * -45));
}
function resizeBgCanvas() {
bgCanvas.width = window.innerWidth;
bgCanvas.height = window.innerHeight;
initFluidParticles();
initMatrixDrops();
}
window.addEventListener('resize', resizeBgCanvas);
resizeBgCanvas();
// Audio frequency extractor
function getAudioBeat() {
if (!isPlaying || !analyser) return { bass: 0, mid: 0, energy: 0 };
const freqData = new Uint8Array(analyser.frequencyBinCount);
analyser.getByteFrequencyData(freqData);
let bSum = 0; for (let i = 0; i < 5; i++) bSum += freqData[i];
let mSum = 0; for (let i = 5; i < 20; i++) mSum += freqData[i];
const bass = (bSum / 5) / 255;
const mid = (mSum / 15) / 255;
return { bass, mid, energy: (bass * 0.65 + mid * 0.35) };
}
let lastAudioBeat = 0;
let gridOffset = 0;
function renderBgFrame(timestamp) {
requestAnimationFrame(renderBgFrame);
const W = bgCanvas.width;
const H = bgCanvas.height;
if (W === 0 || H === 0) return;
// Mouse velocity & position tracking
bgMouse.vx = (bgMouse.targetX - bgMouse.x);
bgMouse.vy = (bgMouse.targetY - bgMouse.y);
bgMouse.x += bgMouse.vx * 0.1;
bgMouse.y += bgMouse.vy * 0.1;
const audio = getAudioBeat();
// Audio beat drop shockwave
if (audio.bass > 0.68 && timestamp - lastAudioBeat > 320) {
lastAudioBeat = timestamp;
shockwaves.push({
x: W / 2,
y: H / 2,
radius: 12,
maxRadius: Math.max(W, H) * 0.75,
alpha: 0.5,
power: 1.5
});
}
// Update shockwaves
for (let i = shockwaves.length - 1; i >= 0; i--) {
const sw = shockwaves[i];
sw.radius += 6.5;
sw.alpha *= 0.955;
if (sw.radius > sw.maxRadius || sw.alpha < 0.01) {
shockwaves.splice(i, 1);
}
}
const currentMode = bgModes[currentBgModeIdx].id;
if (currentMode === 'video-ambient') {
bgCtx.clearRect(0, 0, W, H);
return;
}
if (currentMode === 'matrix-rain') {
// High-definition monochrome matrix rain
bgCtx.fillStyle = 'rgba(0, 0, 0, 0.09)';
bgCtx.fillRect(0, 0, W, H);
bgCtx.font = '12px "JetBrains Mono", monospace';
for (let i = 0; i < mDrops.length; i++) {
const char = mChars.charAt(Math.floor(Math.random() * mChars.length));
const x = i * 18;
const y = mDrops[i] * 18;
if (y >= 0) {
bgCtx.fillStyle = '#ffffff';
bgCtx.shadowColor = '#ffffff';
bgCtx.shadowBlur = 8;
bgCtx.fillText(char, x, y);
bgCtx.shadowBlur = 0;
bgCtx.fillStyle = 'rgba(210, 210, 210, 0.45)';
const prevChar = mChars.charAt(Math.floor(Math.random() * mChars.length));
bgCtx.fillText(prevChar, x, y - 18);
}
if (y > H && Math.random() > 0.978) mDrops[i] = 0;
mDrops[i]++;
}
return;
}
if (currentMode === 'liquid-void') {
// ── "Liquid Void" Incompressible Kinetic Particle Fluid ─────────────
bgCtx.fillStyle = 'rgba(0, 0, 0, 0.14)';
bgCtx.fillRect(0, 0, W, H);
// Render expanding shockwaves
for (let i = 0; i < shockwaves.length; i++) {
const sw = shockwaves[i];
bgCtx.strokeStyle = `rgba(255, 255, 255, ${sw.alpha * 0.35})`;
bgCtx.lineWidth = 1.2;
bgCtx.beginPath();
bgCtx.arc(sw.x, sw.y, sw.radius, 0, Math.PI * 2);
bgCtx.stroke();
}
const flowSpeed = (1.4 + audio.bass * 2.2);
for (let i = 0; i < fluidParticles.length; i++) {
const p = fluidParticles[i];
p.oldX = p.x;
p.oldY = p.y;
// 1. Natural Incompressible Curl Flow Field
const curl = getCurlVector(p.x, p.y, timestamp);
p.vx += curl.vx * 0.22 * p.speed;
p.vy += curl.vy * 0.22 * p.speed;
// 2. Interactive Mouse Vortex & Kinetic Attraction
if (bgMouse.active) {
const mdx = bgMouse.x - p.x;
const mdy = bgMouse.y - p.y;
const mDist = Math.hypot(mdx, mdy);
if (mDist < 220 && mDist > 0.001) {
const factor = (1 - mDist / 220);
// Tangential vortex whirlpool force
const swirlX = -mdy / mDist;
const swirlY = mdx / mDist;
p.vx += swirlX * factor * 2.8;
p.vy += swirlY * factor * 2.8;
// Gentle radial pull into the vortex center
p.vx += (mdx / mDist) * factor * 1.1;
p.vy += (mdy / mDist) * factor * 1.1;
// Kinetic velocity transfer from rapid cursor movement
p.vx += bgMouse.vx * factor * 0.18;
p.vy += bgMouse.vy * factor * 0.18;
}
}
// 3. Shockwave Impulse on particles
for (let k = 0; k < shockwaves.length; k++) {
const sw = shockwaves[k];
const sdx = p.x - sw.x;
const sdy = p.y - sw.y;
const sDist = Math.hypot(sdx, sdy);
const ringDist = Math.abs(sDist - sw.radius);
if (ringDist < 45 && sDist > 0.001) {
const push = (1 - ringDist / 45) * 3.5 * sw.alpha * sw.power;
p.vx += (sdx / sDist) * push;
p.vy += (sdy / sDist) * push;
}
}
// Fluid viscosity / drag
p.vx *= 0.92;
p.vy *= 0.92;
p.x += p.vx * flowSpeed;
p.y += p.vy * flowSpeed;
// Boundary wrapping with clean respawn
p.life++;
if (p.x < -10 || p.x > W + 10 || p.y < -10 || p.y > H + 10 || p.life > p.maxLife) {
p.x = Math.random() * W;
p.y = Math.random() * H;
p.oldX = p.x;
p.oldY = p.y;
p.vx = 0;
p.vy = 0;
p.life = 0;
}
// 4. Render Silky Luminous Trail
const vMag = Math.hypot(p.vx, p.vy);
const glowAlpha = Math.min(1, p.alpha * (0.8 + audio.bass * 0.9) + vMag * 0.08);
bgCtx.strokeStyle = `rgba(255, 255, 255, ${glowAlpha})`;
bgCtx.lineWidth = p.size * (1 + audio.bass * 0.45);
bgCtx.beginPath();
bgCtx.moveTo(p.oldX, p.oldY);
bgCtx.lineTo(p.x, p.y);
bgCtx.stroke();
// Bright stardust head point
bgCtx.fillStyle = `rgba(255, 255, 255, ${Math.min(1, glowAlpha + 0.3)})`;
bgCtx.fillRect(p.x - p.size / 2, p.y - p.size / 2, p.size, p.size);
}
return;
}
if (currentMode === 'cyber-horizon') {
// ── Clean 3D Perspective Horizon (Zero text or clutter) ──────────────
bgCtx.fillStyle = 'rgba(0, 0, 0, 0.22)';
bgCtx.fillRect(0, 0, W, H);
const horizonY = H * 0.65;
// Horizon glow
const hGrad = bgCtx.createLinearGradient(0, horizonY - 35, 0, horizonY + 25);
hGrad.addColorStop(0, 'rgba(255, 255, 255, 0)');
hGrad.addColorStop(0.5, `rgba(255, 255, 255, ${0.1 + audio.bass * 0.2})`);
hGrad.addColorStop(1, 'rgba(255, 255, 255, 0)');
bgCtx.fillStyle = hGrad;
bgCtx.fillRect(0, horizonY - 35, W, 60);
// Horizon sharp divider
bgCtx.strokeStyle = `rgba(255, 255, 255, ${0.4 + audio.bass * 0.4})`;
bgCtx.lineWidth = 1;
bgCtx.beginPath();
bgCtx.moveTo(0, horizonY);
bgCtx.lineTo(W, horizonY);
bgCtx.stroke();
// Longitudinal radiating lines
const lineCount = 28;
const centerX = W / 2;
const bottomSpread = W * 1.4;
bgCtx.lineWidth = 1;
for (let i = -lineCount / 2; i <= lineCount / 2; i++) {
const topX = centerX + i * 16;
const bottomX = centerX + (i / (lineCount / 2)) * (bottomSpread / 2);
const lineAlpha = 0.04 + (1 - Math.abs(i) / (lineCount / 2)) * 0.12 + audio.bass * 0.08;
bgCtx.strokeStyle = `rgba(255, 255, 255, ${lineAlpha})`;
bgCtx.beginPath();
bgCtx.moveTo(topX, horizonY);
bgCtx.lineTo(bottomX, H);
bgCtx.stroke();
}
// Transverse rolling perspective lines
gridOffset = (gridOffset + 0.005 + audio.bass * 0.01) % 1;
const transLines = 14;
for (let k = 0; k < transLines; k++) {
const progress = (k + gridOffset) / transLines;
const py = horizonY + (H - horizonY) * Math.pow(progress, 2.4);
const tAlpha = Math.min(0.35, progress * 0.38 + audio.bass * 0.15);
bgCtx.strokeStyle = `rgba(255, 255, 255, ${tAlpha})`;
bgCtx.beginPath();
const stepX = 20;
for (let x = 0; x <= W; x += stepX) {
const wave = Math.sin(x * 0.007 + timestamp * 0.003) * audio.bass * 14 * progress;
if (x === 0) bgCtx.moveTo(x, py + wave);
else bgCtx.lineTo(x, py + wave);
}
bgCtx.stroke();
}
}
}
requestAnimationFrame(renderBgFrame);
applyBgMode(true);
// ── Desktop Right-Click Context Menu ─────────────────────────────────────
const ctxMenu = document.getElementById('ctxMenu');
document.addEventListener('contextmenu', e => {
if (e.target.closest('.window-body') || e.target.closest('input')) return;
e.preventDefault();
ctxMenu.style.display = 'block';
let x = e.clientX;
let y = e.clientY;
if (x + 190 > window.innerWidth) x = window.innerWidth - 195;
if (y + 200 > window.innerHeight) y = window.innerHeight - 205;
ctxMenu.style.left = x + 'px';
ctxMenu.style.top = y + 'px';
});
document.addEventListener('click', e => {
if (!e.target.closest('.context-menu')) {
ctxMenu.style.display = 'none';
}
});
// ── Clock ────────────────────────────────────────────────────────────────
function updateClock() {
const now = new Date();
const h = String(now.getHours()).padStart(2, '0');
const m = String(now.getMinutes()).padStart(2, '0');
const s = String(now.getSeconds()).padStart(2, '0');
document.getElementById('clock').textContent = `${h}:${m}:${s}`;
}
updateClock();
setInterval(updateClock, 1000);
// ── Background Video Loader ──────────────────────────────────────────────
async function tryLoadBgVideo() {
if (bgModes[currentBgModeIdx].id !== 'video-ambient') return;
for (const name of ['backgroundVideo.mp4', 'backgroundVideo.webm']) {
try {
const res = await fetch(name);
if (res.ok) {
const url = URL.createObjectURL(await res.blob());
bgVideo.src = url;
bgVideo.load();
bgVideo.play().catch(() => {});
return;
}
} catch (e) {}
}
}
// ── Loading Screen Enter ─────────────────────────────────────────────────
function enterKatOS() {
const loadingScreen = document.getElementById('loadingScreen');
if (loadingScreen && !loadingScreen.classList.contains('hidden')) {
loadingScreen.classList.add('hidden');
applyBgMode(true);
setTimeout(() => {
playMusic();
focusTermInput();
}, 250);
}
}
window.enterKatOS = enterKatOS;
const loadingScreen = document.getElementById('loadingScreen');
if (loadingScreen) {
loadingScreen.addEventListener('click', enterKatOS);
}
// Keyboard Shortcut (Space to play/pause music, Enter/Space to enter from boot screen)
window.addEventListener('keydown', e => {
const loadingEl = document.getElementById('loadingScreen');
if (loadingEl && !loadingEl.classList.contains('hidden')) {
e.preventDefault();
enterKatOS();
return;
}
if (e.target.tagName === 'INPUT' || e.target.tagName === 'TEXTAREA') return;
if (e.code === 'Space') {
e.preventDefault();
togglePlay();
}
});
// Initialize System
initWindows();
</script>
</body>
</html>