1748 lines
59 KiB
HTML
1748 lines
59 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 -->
|
||
<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=JetBrains+Mono:ital,wght@0,300;0,400;0,500;0,600;0,700;1,400&family=Orbitron:wght@600;800;900&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 */
|
||
}
|
||
|
||
:root {
|
||
--bg: #050505;
|
||
--panel-bg: rgba(10, 10, 12, 0.82);
|
||
--panel-active: rgba(14, 14, 18, 0.94);
|
||
--border: rgba(255, 255, 255, 0.14);
|
||
--border-hover: rgba(255, 255, 255, 0.32);
|
||
--border-active: rgba(255, 255, 255, 0.48);
|
||
--text: #f0f0f0;
|
||
--text-dim: #777777;
|
||
--text-sub: #a0a0a0;
|
||
--accent: #4db8ff;
|
||
--accent-dim: rgba(77, 184, 255, 0.18);
|
||
--yt: #ff4444;
|
||
--ig: #e1306c;
|
||
--taskbar-h: 36px;
|
||
}
|
||
|
||
body {
|
||
background: var(--bg);
|
||
color: var(--text);
|
||
font-family: 'JetBrains Mono', monospace;
|
||
height: 100vh;
|
||
width: 100vw;
|
||
overflow: hidden;
|
||
user-select: none;
|
||
-webkit-font-smoothing: antialiased;
|
||
}
|
||
|
||
/* ── Background Video & Matrix Layer ────────────────────────────────────── */
|
||
.video-bg {
|
||
position: fixed;
|
||
inset: 0;
|
||
width: 100%;
|
||
height: 100%;
|
||
object-fit: cover;
|
||
z-index: 0;
|
||
pointer-events: none;
|
||
opacity: 0.35;
|
||
filter: contrast(1.1) brightness(0.9);
|
||
}
|
||
|
||
#matrixCanvas {
|
||
position: fixed;
|
||
inset: 0;
|
||
width: 100%;
|
||
height: 100%;
|
||
pointer-events: none;
|
||
z-index: 1;
|
||
opacity: 0;
|
||
transition: opacity 0.5s ease;
|
||
}
|
||
|
||
#matrixCanvas.active {
|
||
opacity: 0.38;
|
||
}
|
||
|
||
.desktop-grid {
|
||
position: fixed;
|
||
inset: 0;
|
||
background-image:
|
||
linear-gradient(rgba(255, 255, 255, 0.025) 1px, transparent 1px),
|
||
linear-gradient(90deg, rgba(255, 255, 255, 0.025) 1px, transparent 1px);
|
||
background-size: 40px 40px;
|
||
pointer-events: none;
|
||
z-index: 1;
|
||
}
|
||
|
||
/* ── Desktop Canvas Area ─────────────────────────────────────────────────── */
|
||
.desktop {
|
||
position: relative;
|
||
width: 100%;
|
||
height: calc(100vh - var(--taskbar-h));
|
||
z-index: 2;
|
||
overflow: hidden;
|
||
}
|
||
|
||
/* ── Windows (Squared Terminals) ─────────────────────────────────────────── */
|
||
.window {
|
||
position: absolute;
|
||
background: var(--panel-bg);
|
||
border: 1px solid var(--border);
|
||
backdrop-filter: blur(28px) saturate(190%) brightness(1.05);
|
||
-webkit-backdrop-filter: blur(28px) saturate(190%) brightness(1.05);
|
||
box-shadow: 0 10px 40px rgba(0, 0, 0, 0.65), 0 0 1px rgba(255, 255, 255, 0.15) inset;
|
||
z-index: 10;
|
||
display: flex;
|
||
flex-direction: column;
|
||
transition: opacity 0.2s ease, transform 0.2s ease, border-color 0.2s ease, box-shadow 0.2s ease;
|
||
}
|
||
|
||
.window.active {
|
||
border-color: var(--border-active);
|
||
background: var(--panel-active);
|
||
box-shadow: 0 14px 50px rgba(0, 0, 0, 0.85), 0 0 16px rgba(255, 255, 255, 0.08), 0 0 1px rgba(255, 255, 255, 0.25) inset;
|
||
z-index: 30;
|
||
}
|
||
|
||
.window.minimized {
|
||
display: none;
|
||
}
|
||
|
||
.window.maximized {
|
||
top: 0 !important;
|
||
left: 0 !important;
|
||
width: 100% !important;
|
||
height: 100% !important;
|
||
}
|
||
|
||
/* Titlebar */
|
||
.titlebar {
|
||
height: 32px;
|
||
min-height: 32px;
|
||
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;
|
||
}
|
||
|
||
.window.active .title-left {
|
||
color: #ffffff;
|
||
}
|
||
|
||
.title-left i {
|
||
font-size: 11px;
|
||
opacity: 0.7;
|
||
}
|
||
|
||
/* Squared Window Controls */
|
||
.window-controls {
|
||
display: flex;
|
||
align-items: center;
|
||
gap: 6px;
|
||
}
|
||
|
||
.win-btn {
|
||
width: 12px;
|
||
height: 12px;
|
||
cursor: pointer;
|
||
display: inline-flex;
|
||
align-items: center;
|
||
justify-content: center;
|
||
font-size: 9px;
|
||
font-weight: 700;
|
||
color: #000;
|
||
transition: all 0.12s ease;
|
||
opacity: 0.85;
|
||
line-height: 1;
|
||
}
|
||
|
||
.win-btn:hover {
|
||
opacity: 1;
|
||
transform: scale(1.1);
|
||
}
|
||
|
||
.win-btn-close {
|
||
background: #ff5f56;
|
||
border: 1px solid #e0443e;
|
||
}
|
||
|
||
.win-btn-min {
|
||
background: #ffbd2e;
|
||
border: 1px solid #dea123;
|
||
}
|
||
|
||
.win-btn-max {
|
||
background: #27c93f;
|
||
border: 1px solid #1aab29;
|
||
}
|
||
|
||
.win-btn span {
|
||
display: none;
|
||
}
|
||
|
||
.titlebar:hover .win-btn span {
|
||
display: block;
|
||
}
|
||
|
||
/* Window Body Content */
|
||
.window-body {
|
||
flex: 1;
|
||
padding: 14px 16px;
|
||
font-size: 12px;
|
||
line-height: 1.6;
|
||
overflow-y: auto;
|
||
overflow-x: hidden;
|
||
color: #d8d8d8;
|
||
}
|
||
|
||
/* Custom Squared Scrollbar */
|
||
.window-body::-webkit-scrollbar,
|
||
.terminal-body::-webkit-scrollbar {
|
||
width: 6px;
|
||
height: 6px;
|
||
}
|
||
.window-body::-webkit-scrollbar-track,
|
||
.terminal-body::-webkit-scrollbar-track {
|
||
background: rgba(0, 0, 0, 0.4);
|
||
}
|
||
.window-body::-webkit-scrollbar-thumb,
|
||
.terminal-body::-webkit-scrollbar-thumb {
|
||
background: rgba(255, 255, 255, 0.18);
|
||
}
|
||
.window-body::-webkit-scrollbar-thumb:hover,
|
||
.terminal-body::-webkit-scrollbar-thumb:hover {
|
||
background: rgba(255, 255, 255, 0.35);
|
||
}
|
||
|
||
/* ── Terminal Styling ────────────────────────────────────────────────────── */
|
||
.terminal-body {
|
||
font-family: 'JetBrains Mono', monospace;
|
||
font-size: 11px;
|
||
line-height: 1.5;
|
||
padding: 12px 14px;
|
||
color: #e5e5e5;
|
||
background: #08080a;
|
||
overflow-y: auto;
|
||
height: 100%;
|
||
user-select: text;
|
||
}
|
||
|
||
.fastfetch-pre {
|
||
white-space: pre;
|
||
line-height: 1.38;
|
||
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: var(--accent);
|
||
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: #bbb;
|
||
line-height: 1.5;
|
||
white-space: pre-wrap;
|
||
}
|
||
|
||
.term-cmd {
|
||
color: #fff;
|
||
font-weight: 500;
|
||
}
|
||
|
||
/* Links & Lists */
|
||
.link-item {
|
||
display: flex;
|
||
align-items: center;
|
||
gap: 10px;
|
||
padding: 7px 10px;
|
||
margin-bottom: 6px;
|
||
background: rgba(255, 255, 255, 0.025);
|
||
border: 1px solid rgba(255, 255, 255, 0.08);
|
||
cursor: pointer;
|
||
font-size: 12px;
|
||
color: #ddd;
|
||
transition: all 0.15s ease;
|
||
text-decoration: none;
|
||
}
|
||
|
||
.link-item:hover {
|
||
background: rgba(255, 255, 255, 0.08);
|
||
border-color: var(--border-hover);
|
||
color: #fff;
|
||
transform: translateX(4px);
|
||
}
|
||
|
||
.link-item i {
|
||
width: 16px;
|
||
text-align: center;
|
||
color: var(--accent);
|
||
}
|
||
|
||
.link-item .badge {
|
||
margin-left: auto;
|
||
font-size: 10px;
|
||
color: var(--text-dim);
|
||
border: 1px solid #222;
|
||
padding: 1px 6px;
|
||
background: #080808;
|
||
}
|
||
|
||
.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;
|
||
}
|
||
|
||
.skill-tag:hover {
|
||
border-color: var(--accent);
|
||
color: var(--accent);
|
||
}
|
||
|
||
/* ── Audio Visualizer ────────────────────────────────────────────────────── */
|
||
.viz-container {
|
||
width: 100%;
|
||
height: 100%;
|
||
position: relative;
|
||
background: #060608;
|
||
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: 12px;
|
||
pointer-events: none;
|
||
font-size: 10px;
|
||
letter-spacing: 0.08em;
|
||
color: rgba(255, 255, 255, 0.35);
|
||
text-transform: uppercase;
|
||
}
|
||
|
||
.viz-mode-badge {
|
||
padding: 2px 6px;
|
||
border: 1px solid rgba(255, 255, 255, 0.15);
|
||
background: rgba(0, 0, 0, 0.5);
|
||
color: var(--accent);
|
||
}
|
||
|
||
.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(8, 8, 10, 0.96);
|
||
border-top: 1px solid var(--border);
|
||
backdrop-filter: blur(20px);
|
||
z-index: 100;
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: space-between;
|
||
padding: 0 10px;
|
||
font-size: 11px;
|
||
}
|
||
|
||
.taskbar-left {
|
||
display: flex;
|
||
align-items: center;
|
||
gap: 8px;
|
||
height: 100%;
|
||
}
|
||
|
||
.os-logo-btn {
|
||
font-family: 'Orbitron', sans-serif;
|
||
font-weight: 800;
|
||
font-size: 12px;
|
||
letter-spacing: 0.06em;
|
||
color: #fff;
|
||
padding: 4px 10px;
|
||
border: 1px solid rgba(255, 255, 255, 0.16);
|
||
background: rgba(255, 255, 255, 0.04);
|
||
cursor: pointer;
|
||
display: flex;
|
||
align-items: center;
|
||
gap: 6px;
|
||
transition: all 0.15s ease;
|
||
}
|
||
|
||
.os-logo-btn:hover {
|
||
background: rgba(255, 255, 255, 0.12);
|
||
border-color: #fff;
|
||
}
|
||
|
||
.os-logo-btn i {
|
||
color: var(--accent);
|
||
font-size: 11px;
|
||
}
|
||
|
||
.taskbar-divider {
|
||
width: 1px;
|
||
height: 18px;
|
||
background: rgba(255, 255, 255, 0.1);
|
||
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.06);
|
||
color: #fff;
|
||
border-color: var(--border-hover);
|
||
}
|
||
|
||
.taskbar-win-btn.active {
|
||
background: rgba(255, 255, 255, 0.09);
|
||
border-color: var(--border-active);
|
||
color: #fff;
|
||
border-bottom: 2px solid var(--accent);
|
||
}
|
||
|
||
.taskbar-right {
|
||
display: flex;
|
||
align-items: center;
|
||
gap: 12px;
|
||
}
|
||
|
||
/* Music Player Taskbar Widget */
|
||
.music-bar {
|
||
display: flex;
|
||
align-items: center;
|
||
gap: 8px;
|
||
padding: 2px 8px;
|
||
border: 1px solid rgba(255, 255, 255, 0.08);
|
||
background: rgba(255, 255, 255, 0.02);
|
||
}
|
||
|
||
.music-icon {
|
||
font-size: 11px;
|
||
color: var(--accent);
|
||
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: 150px;
|
||
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.15);
|
||
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;
|
||
}
|
||
|
||
.volume-slider {
|
||
width: 50px;
|
||
height: 3px;
|
||
-webkit-appearance: none;
|
||
appearance: none;
|
||
background: rgba(255, 255, 255, 0.2);
|
||
outline: none;
|
||
}
|
||
|
||
.volume-slider::-webkit-slider-thumb {
|
||
-webkit-appearance: none;
|
||
appearance: none;
|
||
width: 8px;
|
||
height: 12px;
|
||
background: #ffffff;
|
||
cursor: pointer;
|
||
}
|
||
|
||
/* Tray Info */
|
||
.tray-widget {
|
||
display: flex;
|
||
align-items: center;
|
||
gap: 10px;
|
||
font-size: 11px;
|
||
color: var(--text-dim);
|
||
}
|
||
|
||
.tray-time {
|
||
color: #ffffff;
|
||
font-weight: 500;
|
||
}
|
||
|
||
/* ── Desktop Context Menu ────────────────────────────────────────────────── */
|
||
.context-menu {
|
||
position: fixed;
|
||
z-index: 9999;
|
||
background: rgba(12, 12, 14, 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.08);
|
||
color: var(--accent);
|
||
}
|
||
|
||
.ctx-item i {
|
||
width: 14px;
|
||
font-size: 11px;
|
||
}
|
||
|
||
.ctx-sep {
|
||
height: 1px;
|
||
background: rgba(255, 255, 255, 0.08);
|
||
margin: 4px 0;
|
||
}
|
||
|
||
/* ── Loading 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-box {
|
||
border: 1px solid #333;
|
||
background: #08080a;
|
||
padding: 36px 48px;
|
||
max-width: 520px;
|
||
width: 90%;
|
||
text-align: center;
|
||
box-shadow: 0 0 50px rgba(0, 0, 0, 0.9);
|
||
}
|
||
|
||
.boot-logo {
|
||
font-family: 'Orbitron', sans-serif;
|
||
font-size: 48px;
|
||
font-weight: 900;
|
||
letter-spacing: 0.08em;
|
||
color: #fff;
|
||
text-shadow: 0 0 20px rgba(255, 255, 255, 0.4);
|
||
margin-bottom: 18px;
|
||
}
|
||
|
||
.boot-log {
|
||
font-size: 11px;
|
||
color: #666;
|
||
text-align: left;
|
||
line-height: 1.6;
|
||
margin-bottom: 24px;
|
||
border-top: 1px solid #1a1a1a;
|
||
padding-top: 14px;
|
||
}
|
||
|
||
.boot-btn {
|
||
display: inline-block;
|
||
padding: 10px 24px;
|
||
border: 1px solid #555;
|
||
background: #111;
|
||
color: #fff;
|
||
font-size: 11px;
|
||
letter-spacing: 0.08em;
|
||
text-transform: uppercase;
|
||
cursor: pointer;
|
||
animation: pulseBtn 1.8s infinite alternate;
|
||
}
|
||
|
||
@keyframes pulseBtn {
|
||
from { border-color: #444; color: #aaa; }
|
||
to { border-color: var(--accent); color: var(--accent); box-shadow: 0 0 14px rgba(77, 184, 255, 0.3); }
|
||
}
|
||
|
||
/* ── Responsive Window Bounds for Small Screens ──────────────────────────── */
|
||
@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>
|
||
|
||
<!-- Loading Screen (Click to Start Audio & Video) -->
|
||
<div id="loadingScreen">
|
||
<div class="boot-box">
|
||
<div class="boot-logo">katOS</div>
|
||
<div class="boot-log">
|
||
<div>[ <span style="color:#22c55e;">OK</span> ] Mounted /dev/nvme0n1p2 on /home/oiupoyt</div>
|
||
<div>[ <span style="color:#22c55e;">OK</span> ] Audio subsystem initialized: Baiaa081</div>
|
||
<div>[ <span style="color:#22c55e;">OK</span> ] Wayland compositor: Hyprland 0.54.2</div>
|
||
<div>[ <span style="color:#22c55e;">OK</span> ] Fastfetch terminal ready</div>
|
||
</div>
|
||
<div class="boot-btn">[ click anywhere to boot ]</div>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- Background Video & Ambient Canvas Layers -->
|
||
<video class="video-bg" id="bgVideo" autoplay muted loop playsinline></video>
|
||
<canvas id="matrixCanvas"></canvas>
|
||
<div class="desktop-grid"></div>
|
||
|
||
<!-- Desktop Workspace -->
|
||
<div class="desktop" id="desktop">
|
||
|
||
<!-- About Window -->
|
||
<div class="window active" id="win-about" style="top: 30px; left: 36px; width: 330px;">
|
||
<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-btn win-btn-min" onclick="minimizeWindow('win-about')"><span>–</span></div>
|
||
<div class="win-btn win-btn-max" onclick="toggleMaximize('win-about')"><span>□</span></div>
|
||
<div class="win-btn win-btn-close" onclick="closeWindow('win-about')"><span>×</span></div>
|
||
</div>
|
||
</div>
|
||
<div class="window-body">
|
||
<p style="color:#fff; 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:#666; margin-bottom: 12px; font-family:monospace;">i was on the ███████████████</p>
|
||
<p style="color:#4db8ff; margin-bottom: 12px;">cats > people haha.</p>
|
||
<p style="color:#eee; margin-bottom: 12px;">i use arch yea</p>
|
||
<p style="color:#ffffff; font-weight:600;">you da real kat 🤍.</p>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- Skills Window -->
|
||
<div class="window" id="win-skills" style="top: 250px; left: 36px; width: 330px;">
|
||
<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-btn win-btn-min" onclick="minimizeWindow('win-skills')"><span>–</span></div>
|
||
<div class="win-btn win-btn-max" onclick="toggleMaximize('win-skills')"><span>□</span></div>
|
||
<div class="win-btn win-btn-close" onclick="closeWindow('win-skills')"><span>×</span></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: 12px 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" style="top: 30px; left: 390px; width: 340px;">
|
||
<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-btn win-btn-min" onclick="minimizeWindow('win-websites')"><span>–</span></div>
|
||
<div class="win-btn win-btn-max" onclick="toggleMaximize('win-websites')"><span>□</span></div>
|
||
<div class="win-btn win-btn-close" onclick="closeWindow('win-websites')"><span>×</span></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>katdrop</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://kat-images.netlify.app/" target="_blank" rel="noopener">
|
||
<i class="fas fa-cat"></i>
|
||
<div>
|
||
<div>katimages</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://justdownload.netlify.app" target="_blank" rel="noopener">
|
||
<i class="fas fa-download"></i>
|
||
<div>
|
||
<div>just-download</div>
|
||
<div style="font-size:10px; color:#777;">yt & ig media downloader</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>kattoolbox</div>
|
||
<div style="font-size:10px; color:#777;">client-side dev utility suite</div>
|
||
</div>
|
||
<span class="badge">live</span>
|
||
</a>
|
||
<a class="link-item" href="https://github.com/oiupoyt" target="_blank" rel="noopener">
|
||
<i class="fab fa-github"></i>
|
||
<div>
|
||
<div>github / oiupoyt</div>
|
||
<div style="font-size:10px; color:#777;">source code & repositories</div>
|
||
</div>
|
||
<span class="badge">repos</span>
|
||
</a>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- Social Links Window -->
|
||
<div class="window" id="win-links" style="top: 310px; left: 390px; width: 340px;">
|
||
<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-btn win-btn-min" onclick="minimizeWindow('win-links')"><span>–</span></div>
|
||
<div class="win-btn win-btn-max" onclick="toggleMaximize('win-links')"><span>□</span></div>
|
||
<div class="win-btn win-btn-close" onclick="closeWindow('win-links')"><span>×</span></div>
|
||
</div>
|
||
</div>
|
||
<div class="window-body">
|
||
<a class="link-item" href="https://discordapp.com/users/1082968457559146506" target="_blank" rel="noopener">
|
||
<i class="fab fa-discord" style="color:#5865F2;"></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" style="color:#E1306C;"></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" style="color:#FF0000;"></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" style="color:#1BD96A;"></i>
|
||
<span>Modrinth</span>
|
||
<span class="badge">mods</span>
|
||
</a>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- Interactive FastFetch Terminal Window -->
|
||
<div class="window active" id="win-term" style="top: 30px; left: 750px; width: 680px; height: 410px;">
|
||
<div class="titlebar">
|
||
<div class="title-left">
|
||
<i class="fas fa-terminal"></i>
|
||
<span>~fastfetch</span>
|
||
</div>
|
||
<div class="window-controls">
|
||
<div class="win-btn win-btn-min" onclick="minimizeWindow('win-term')"><span>–</span></div>
|
||
<div class="win-btn win-btn-max" onclick="toggleMaximize('win-term')"><span>□</span></div>
|
||
<div class="win-btn win-btn-close" onclick="closeWindow('win-term')"><span>×</span></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" style="top: 460px; left: 750px; width: 680px; height: 320px;">
|
||
<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-btn win-btn-min" onclick="minimizeWindow('win-viz')"><span>–</span></div>
|
||
<div class="win-btn win-btn-max" onclick="toggleMaximize('win-viz')"><span>□</span></div>
|
||
<div class="win-btn win-btn-close" onclick="closeWindow('win-viz')"><span>×</span></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="toggleMenu()" title="katOS Launcher">
|
||
<i class="fas fa-cat"></i>
|
||
<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 -->
|
||
<div class="tray-widget">
|
||
<span title="Memory usage">RAM: 4.2G</span>
|
||
<span class="taskbar-divider"></span>
|
||
<span class="tray-time" id="clock">00:00:00</span>
|
||
</div>
|
||
</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="cycleVizMode()"><i class="fas fa-sliders"></i> Cycle Visualizer Mode</div>
|
||
<div class="ctx-item" onclick="toggleMatrix()"><i class="fas fa-code"></i> Toggle Matrix Rain</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="resetLayout()"><i class="fas fa-rotate-left"></i> Reset Window Layout</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'];
|
||
|
||
// ── Window Management (Draggable, Focus, Min/Max) ────────────────────────
|
||
function bringToFront(win) {
|
||
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-btn')) 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;
|
||
|
||
// Bounds checking
|
||
const maxX = window.innerWidth - 60;
|
||
const maxY = window.innerHeight - 80;
|
||
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);
|
||
});
|
||
|
||
// Double click to maximize
|
||
bar.addEventListener('dblclick', e => {
|
||
if (e.target.closest('.win-btn')) return;
|
||
toggleMaximize(win.id);
|
||
});
|
||
});
|
||
updateTaskbar();
|
||
}
|
||
|
||
function minimizeWindow(id) {
|
||
const win = document.getElementById(id);
|
||
if (!win) return;
|
||
win.classList.add('minimized');
|
||
updateTaskbar();
|
||
}
|
||
|
||
function toggleMaximize(id) {
|
||
const win = document.getElementById(id);
|
||
if (!win) return;
|
||
win.classList.toggle('maximized');
|
||
bringToFront(win);
|
||
}
|
||
|
||
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 resetLayout() {
|
||
restoreAllWindows();
|
||
const layouts = {
|
||
'win-about': { top: '30px', left: '36px', width: '330px', height: 'auto' },
|
||
'win-skills': { top: '250px', left: '36px', width: '330px', height: 'auto' },
|
||
'win-websites': { top: '30px', left: '390px', width: '340px', height: 'auto' },
|
||
'win-links': { top: '310px', left: '390px', width: '340px', height: 'auto' },
|
||
'win-term': { top: '30px', left: '750px', width: '680px', height: '410px' },
|
||
'win-viz': { top: '460px', left: '750px', width: '680px', height: '320px' }
|
||
};
|
||
Object.keys(layouts).forEach(id => {
|
||
const win = document.getElementById(id);
|
||
if (win) {
|
||
Object.assign(win.style, layouts[id]);
|
||
}
|
||
});
|
||
bringToFront(document.getElementById('win-term'));
|
||
}
|
||
|
||
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-window-maximize';
|
||
|
||
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 & System Specs ────────────────────────────────────
|
||
const fastfetchArtEl = document.getElementById('fastfetchArt');
|
||
const fa = cls => `<i class="${cls}" style="width:1.2em;text-align:center;display:inline-block;opacity:0.8;font-size:10px;"></i>`;
|
||
|
||
const fastfetchLines = [
|
||
` ▄`,
|
||
` ▟█▙`,
|
||
` ▟███▙ <b style="color:#4db8ff">oiupoyt</b><span style="opacity:0.4">@</span><b>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> breeze-dark`,
|
||
` ▟█████████▛▀▀▜████████▙ ${fa('fas fa-font')} <span style="opacity:0.45;font-size:10px">Font</span> JetBrains 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 • katimages • toolbox`,
|
||
` ▟██████▀▀▀ ▀▀██████▙`,
|
||
` ▟███▀▘ ▝▀███▙`,
|
||
` ▟▛▀ ▀▜▙`,
|
||
];
|
||
fastfetchArtEl.innerHTML = fastfetchLines.map(l => `<div class="term-row">${l}</div>`).join('');
|
||
|
||
// ── Interactive Terminal CLI ─────────────────────────────────────────────
|
||
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:#4db8ff">fastfetch</b> - display Arch Linux logo & system specs
|
||
<b style="color:#4db8ff">ls</b> - list virtual directory contents
|
||
<b style="color:#4db8ff">cat <file></b> - display contents of a file (e.g. cat about.md)
|
||
<b style="color:#4db8ff">projects</b> - list active web projects & links
|
||
<b style="color:#4db8ff">skills</b> - show competencies & skills
|
||
<b style="color:#4db8ff">music [play|pause|vol]</b> - control background audio
|
||
<b style="color:#4db8ff">catpic</b> - fetch a fresh cat image
|
||
<b style="color:#4db8ff">matrix</b> - toggle digital rain animation
|
||
<b style="color:#4db8ff">whoami</b> - print current session bio
|
||
<b style="color:#4db8ff">clear</b> - clear terminal screen
|
||
<b style="color:#4db8ff">date</b> - show current date and time
|
||
<b style="color:#4db8ff">reboot</b> - restart katOS desktop`;
|
||
break;
|
||
|
||
case 'fastfetch':
|
||
case 'neofetch':
|
||
fastfetchArtEl.style.display = 'block';
|
||
out.textContent = '';
|
||
break;
|
||
|
||
case 'ls':
|
||
out.innerHTML = `<span style="color:#4db8ff">about.md</span> <span style="color:#22c55e">skillz.bash</span> <span style="color:#eab308">websites.html</span> <span style="color:#ec4899">links.zip</span> <span style="color:#aaa">music.mp3</span> <span style="color:#aaa">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• katimages: https://kat-images.netlify.app/\n• just-download: https://justdownload.netlify.app\n• kattoolbox: https://toolbox.oiupoyt.space`;
|
||
} else {
|
||
out.textContent = `cat: ${arg}: Permission denied or file is binary`;
|
||
}
|
||
break;
|
||
|
||
case 'projects':
|
||
out.innerHTML = `katOS Projects:
|
||
1. <a href="https://katdrop.oiupoyt.space" target="_blank" style="color:#4db8ff">katdrop</a> - minimal 10-min file transfer service
|
||
2. <a href="https://kat-images.netlify.app/" target="_blank" style="color:#4db8ff">katimages</a> - look at cat. feel good.
|
||
3. <a href="https://justdownload.netlify.app" target="_blank" style="color:#4db8ff">just-download</a> - youtube & instagram media downloader
|
||
4. <a href="https://toolbox.oiupoyt.space" target="_blank" style="color:#4db8ff">kattoolbox</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':
|
||
out.innerHTML = `Fetching cat image...`;
|
||
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 #333;margin-top:6px;display:block;"><span style="font-size:10px;color:#777;">meow</span>`;
|
||
termBody.scrollTop = termBody.scrollHeight;
|
||
}
|
||
}).catch(() => {
|
||
out.textContent = 'cat error: no cat found :(';
|
||
});
|
||
break;
|
||
|
||
case 'matrix':
|
||
toggleMatrix();
|
||
out.textContent = `matrix digital rain: ${matrixActive ? 'ON' : 'OFF'}`;
|
||
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 'sudo':
|
||
out.textContent = 'user oiupoyt is not in the sudoers file. this incident will be reported to kat.';
|
||
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 => ({
|
||
'&': '&', '<': '<', '>': '>', "'": ''', '"': '"'
|
||
}[t] || t));
|
||
}
|
||
|
||
// ── Audio & Visualizer Engine ────────────────────────────────────────────
|
||
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', 'waveform beam', '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 Modes ───────────────────────────────────────────
|
||
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);
|
||
|
||
// Resting zero line
|
||
vizCtx.strokeStyle = 'rgba(255, 255, 255, 0.08)';
|
||
vizCtx.lineWidth = 1;
|
||
vizCtx.beginPath();
|
||
vizCtx.moveTo(0, H * 0.9);
|
||
vizCtx.lineTo(W, H * 0.9);
|
||
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 Spectrum Bars with floating glowing peaks
|
||
const barCount = 28;
|
||
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(77, 184, 255, ${alpha})`);
|
||
grad.addColorStop(0.5, `rgba(255, 255, 255, ${alpha * 0.7})`);
|
||
grad.addColorStop(1, `rgba(77, 184, 255, ${alpha * 0.1})`);
|
||
|
||
vizCtx.fillStyle = grad;
|
||
vizCtx.fillRect(x, y, barW, bh);
|
||
|
||
// Squared glowing peak cap
|
||
if (bh > 4) {
|
||
vizCtx.fillStyle = `rgba(255, 255, 255, ${Math.min(1, alpha + 0.2)})`;
|
||
vizCtx.fillRect(x, y - 3, barW, 2);
|
||
}
|
||
}
|
||
|
||
// Base line
|
||
vizCtx.strokeStyle = 'rgba(255, 255, 255, 0.1)';
|
||
vizCtx.lineWidth = 1;
|
||
vizCtx.beginPath();
|
||
vizCtx.moveTo(0, baseY);
|
||
vizCtx.lineTo(W, baseY);
|
||
vizCtx.stroke();
|
||
|
||
} else if (mode === 'waveform beam') {
|
||
// Mode 2: Oscilloscope Waveform Beam
|
||
const timeData = new Uint8Array(analyser.fftSize);
|
||
analyser.getByteTimeDomainData(timeData);
|
||
|
||
vizCtx.lineWidth = 2;
|
||
vizCtx.strokeStyle = '#4db8ff';
|
||
vizCtx.shadowColor = 'rgba(77, 184, 255, 0.8)';
|
||
vizCtx.shadowBlur = 10;
|
||
vizCtx.beginPath();
|
||
|
||
const sliceW = W / timeData.length;
|
||
let x = 0;
|
||
|
||
for (let i = 0; i < timeData.length; i++) {
|
||
const v = timeData[i] / 128.0;
|
||
const y = (v * H) / 2;
|
||
if (i === 0) vizCtx.moveTo(x, y);
|
||
else vizCtx.lineTo(x, y);
|
||
x += sliceW;
|
||
}
|
||
|
||
vizCtx.lineTo(W, H / 2);
|
||
vizCtx.stroke();
|
||
vizCtx.shadowBlur = 0;
|
||
|
||
} else if (mode === 'mirror pulse') {
|
||
// Mode 3: 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})`;
|
||
// Right wing
|
||
vizCtx.fillRect(center + i * barW, centerY - bh / 2, barW - 2, bh);
|
||
// Left wing
|
||
vizCtx.fillRect(center - (i + 1) * barW, centerY - bh / 2, barW - 2, bh);
|
||
}
|
||
|
||
vizCtx.strokeStyle = 'rgba(77, 184, 255, 0.4)';
|
||
vizCtx.lineWidth = 1;
|
||
vizCtx.beginPath();
|
||
vizCtx.moveTo(0, centerY);
|
||
vizCtx.lineTo(W, centerY);
|
||
vizCtx.stroke();
|
||
}
|
||
}
|
||
|
||
// ── Matrix Rain Effect ───────────────────────────────────────────────────
|
||
const matrixCanvas = document.getElementById('matrixCanvas');
|
||
const mCtx = matrixCanvas.getContext('2d');
|
||
let matrixActive = false;
|
||
let matrixInterval = null;
|
||
const mChars = '01KATOSOIUPOYTARCHLINUXλπ#$><;:~';
|
||
let mCols, mDrops;
|
||
|
||
function initMatrix() {
|
||
matrixCanvas.width = window.innerWidth;
|
||
matrixCanvas.height = window.innerHeight;
|
||
mCols = Math.floor(matrixCanvas.width / 18);
|
||
mDrops = Array(mCols).fill(1);
|
||
}
|
||
initMatrix();
|
||
window.addEventListener('resize', initMatrix);
|
||
|
||
function drawMatrix() {
|
||
mCtx.fillStyle = 'rgba(0, 0, 0, 0.08)';
|
||
mCtx.fillRect(0, 0, matrixCanvas.width, matrixCanvas.height);
|
||
mCtx.fillStyle = '#4db8ff';
|
||
mCtx.font = '12px "JetBrains Mono", monospace';
|
||
|
||
for (let i = 0; i < mDrops.length; i++) {
|
||
const text = mChars.charAt(Math.floor(Math.random() * mChars.length));
|
||
mCtx.fillText(text, i * 18, mDrops[i] * 18);
|
||
if (mDrops[i] * 18 > matrixCanvas.height && Math.random() > 0.975) {
|
||
mDrops[i] = 0;
|
||
}
|
||
mDrops[i]++;
|
||
}
|
||
}
|
||
|
||
function toggleMatrix() {
|
||
matrixActive = !matrixActive;
|
||
matrixCanvas.classList.toggle('active', matrixActive);
|
||
if (matrixActive) {
|
||
if (!matrixInterval) matrixInterval = setInterval(drawMatrix, 40);
|
||
} else {
|
||
clearInterval(matrixInterval);
|
||
matrixInterval = null;
|
||
mCtx.clearRect(0, 0, matrixCanvas.width, matrixCanvas.height);
|
||
}
|
||
}
|
||
|
||
// ── Desktop 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 & System Monitor ───────────────────────────────────────────────
|
||
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 ──────────────────────────────────────────────
|
||
const bgVideo = document.getElementById('bgVideo');
|
||
async function tryLoadBgVideo() {
|
||
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 ─────────────────────────────────────────────────
|
||
const loadingScreen = document.getElementById('loadingScreen');
|
||
loadingScreen.addEventListener('click', () => {
|
||
loadingScreen.classList.add('hidden');
|
||
tryLoadBgVideo();
|
||
setTimeout(() => {
|
||
playMusic();
|
||
focusTermInput();
|
||
}, 300);
|
||
});
|
||
|
||
// Keyboard Shortcut (Space to play/pause music outside text inputs)
|
||
window.addEventListener('keydown', e => {
|
||
if (e.target.tagName === 'INPUT' || e.target.tagName === 'TEXTAREA') return;
|
||
if (e.code === 'Space') {
|
||
e.preventDefault();
|
||
togglePlay();
|
||
}
|
||
});
|
||
|
||
// Initialize System
|
||
initWindows();
|
||
</script>
|
||
</body>
|
||
</html>
|