Update index.html
This commit is contained in:
858
index.html
858
index.html
@@ -569,9 +569,855 @@
|
|||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
<div class="card">
|
<!-- FLOATING PARTICLES -->
|
||||||
<h2>Titel</h2>
|
<div class="particles" id="particles"></div>
|
||||||
<p>Dit is wat tekst in de kaart.</p>
|
|
||||||
</div>
|
<!-- CURSOR GLOW -->
|
||||||
</body>
|
<div class="cursor-glow" id="cursorGlow"></div>
|
||||||
|
|
||||||
|
<!-- NAV -->
|
||||||
|
<nav id="navbar">
|
||||||
|
<div class="nav-inner">
|
||||||
|
<a href="#" class="nav-logo">
|
||||||
|
<svg width="28" height="28" viewBox="0 0 32 32" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||||
|
<rect x="1" y="1" width="30" height="30" rx="7" fill="#0d1117" stroke="#22c55e" stroke-width="1.5"/>
|
||||||
|
<text x="6" y="21" font-family="JetBrains Mono, monospace" font-size="14" font-weight="700" fill="#22c55e">>_</text>
|
||||||
|
</svg>
|
||||||
|
<span class="nav-logo-text">terminal-chat</span>
|
||||||
|
</a>
|
||||||
|
<div class="nav-links">
|
||||||
|
<a href="#features" class="nav-link">Features</a>
|
||||||
|
<a href="#docs" class="nav-link">Docs</a>
|
||||||
|
<a href="https://git.de-roo.org/ben/pychat" target="_blank" rel="noopener noreferrer" class="nav-link-git">
|
||||||
|
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M15 22v-4a4.8 4.8 0 00-1-3.5c3 0 6-2 6-5.5.08-1.25-.27-2.48-1-3.5.28-1.15.28-2.35 0-3.5 0 0-1 0-3 1.5-2.64-.5-5.36-.5-8 0C6 2 5 2 5 2c-.3 1.15-.3 2.35 0 3.5A5.403 5.403 0 004 9c0 3.5 3 5.5 6 5.5-.39.49-.68 1.05-.85 1.65-.17.6-.22 1.23-.15 1.85v4"/><path d="M9 18c-4.51 2-5-2-7-2"/></svg>
|
||||||
|
Source Code
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</nav>
|
||||||
|
|
||||||
|
<!-- HERO -->
|
||||||
|
<section class="hero">
|
||||||
|
<div class="hero-badge"><span class="dot"></span> v1.0 — Open Source</div>
|
||||||
|
<h1>Chat from your <span class="highlight">Terminal</span></h1>
|
||||||
|
<p>A simple yet powerful terminal-based chat application written in Python. Realtime messaging via TCP sockets, straight from your command line.</p>
|
||||||
|
<div class="hero-actions">
|
||||||
|
<a href="#demo" class="btn btn-primary">
|
||||||
|
<svg width="16" height="16" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="2"><path d="M5 3l8 5-8 5V3z"/></svg>
|
||||||
|
Live Demo
|
||||||
|
</a>
|
||||||
|
<a href="#docs" class="btn btn-secondary">
|
||||||
|
<svg width="16" height="16" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="2"><path d="M3 2h7l3 3v9H3V2z"/><path d="M10 2v3h3"/></svg>
|
||||||
|
Documentation
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Install Command -->
|
||||||
|
<div class="install-box" id="installatie">
|
||||||
|
<div class="install-header">
|
||||||
|
<div class="install-dots"><span></span><span></span><span></span></div>
|
||||||
|
<span class="install-label">bash</span>
|
||||||
|
<button class="install-copy" id="copyBtn" onclick="copyInstall()" aria-label="Copy command">
|
||||||
|
<svg id="copyIcon" width="16" height="16" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5"><rect x="5" y="5" width="9" height="9" rx="1.5"/><path d="M5 11H3.5A1.5 1.5 0 012 9.5v-7A1.5 1.5 0 013.5 1h7A1.5 1.5 0 0112 2.5V5"/></svg>
|
||||||
|
<svg id="checkIcon" width="16" height="16" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="2" style="display:none"><path d="M3 8.5l3.5 3.5L13 4.5"/></svg>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
<div class="install-body">
|
||||||
|
<span class="prompt">$</span>
|
||||||
|
<span id="installCmd">curl -s https://ben.de-roo.org/install/script.sh | bash</span>
|
||||||
|
</div>
|
||||||
|
<div style="padding:0 20px 16px;display:flex;align-items:center;gap:10px;">
|
||||||
|
<div style="flex:1;height:1px;background:var(--border);"></div>
|
||||||
|
<span style="font-size:12px;color:var(--text-muted);font-family:'JetBrains Mono',monospace;">or</span>
|
||||||
|
<div style="flex:1;height:1px;background:var(--border);"></div>
|
||||||
|
</div>
|
||||||
|
<div style="padding:0 20px 20px;">
|
||||||
|
<a href="https://git.de-roo.org/ben/pychat" target="_blank" rel="noopener noreferrer" style="display:flex;align-items:center;justify-content:center;gap:10px;padding:10px 16px;border-radius:8px;background:rgba(34,197,94,0.08);border:1px solid rgba(34,197,94,0.15);text-decoration:none;transition:all 0.2s;font-family:'JetBrains Mono',monospace;font-size:13px;color:var(--green);"
|
||||||
|
onmouseover="this.style.background='rgba(34,197,94,0.15)';this.style.borderColor='rgba(34,197,94,0.3)'"
|
||||||
|
onmouseout="this.style.background='rgba(34,197,94,0.08)';this.style.borderColor='rgba(34,197,94,0.15)'">
|
||||||
|
<svg width="16" height="16" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5"><path d="M8 2v8m0 0l-3-3m3 3l3-3M3 13h10"/></svg>
|
||||||
|
Download ZIP from git.de-roo.org
|
||||||
|
<svg width="14" height="14" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5" style="opacity:0.5"><path d="M5 3h8v8M13 3L6 10"/></svg>
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<!-- LIVE TERMINAL DEMO -->
|
||||||
|
<section class="terminal-section" id="demo">
|
||||||
|
<div class="terminal-window">
|
||||||
|
<div class="terminal-bar">
|
||||||
|
<div class="terminal-bar-dots"><span></span><span></span><span></span></div>
|
||||||
|
<div class="terminal-bar-title">terminal-chat — python3</div>
|
||||||
|
<div></div>
|
||||||
|
</div>
|
||||||
|
<div class="terminal-body" id="terminalBody" style="padding:0;min-height:400px;display:flex;flex-direction:column;">
|
||||||
|
|
||||||
|
<!-- PHASE 1: Login flow -->
|
||||||
|
<div id="loginPhase" style="padding:20px;">
|
||||||
|
<div class="terminal-line" data-delay="0">
|
||||||
|
<span class="t-dim">$</span> <span class="t-green">python3</span> client.py
|
||||||
|
</div>
|
||||||
|
<div class="terminal-line" data-delay="500">
|
||||||
|
<br>
|
||||||
|
<div style="background:#2563eb;text-align:center;padding:6px 0;border-radius:4px;margin-bottom:4px;">
|
||||||
|
<span class="t-bold" style="color:#fff;">Terminal Chat</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="terminal-line" data-delay="900">
|
||||||
|
<br>
|
||||||
|
<span class="t-green t-bold">========================================</span><br>
|
||||||
|
<span class="t-green t-bold"> Welcome to The Terminal Chat</span><br>
|
||||||
|
<span class="t-green t-bold">========================================</span>
|
||||||
|
</div>
|
||||||
|
<div class="terminal-line" data-delay="1400">
|
||||||
|
<br>
|
||||||
|
<span>1. Login</span><br>
|
||||||
|
<span>2. New account</span>
|
||||||
|
</div>
|
||||||
|
<div class="terminal-line" data-delay="2000">
|
||||||
|
<br><span class="t-dim">></span> <span class="t-yellow">1</span>
|
||||||
|
</div>
|
||||||
|
<div class="terminal-line" data-delay="2500">
|
||||||
|
<br><span class="t-cyan">Username:</span> <span class="t-yellow">max</span>
|
||||||
|
</div>
|
||||||
|
<div class="terminal-line" data-delay="2900">
|
||||||
|
<span class="t-cyan">Password:</span> <span class="t-dim">********</span>
|
||||||
|
</div>
|
||||||
|
<div class="terminal-line" data-delay="3400">
|
||||||
|
<br><span class="t-dim">--- Available chats: ---</span><br><br>
|
||||||
|
<span class="t-cyan">1.</span> <span>General</span><br>
|
||||||
|
<span class="t-cyan">2.</span> <span>Create new chat</span>
|
||||||
|
</div>
|
||||||
|
<div class="terminal-line" data-delay="4000">
|
||||||
|
<br><span class="t-dim">></span> <span class="t-yellow">1</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- PHASE 2: Chat view (hidden initially, shown after login) -->
|
||||||
|
<div id="chatPhase" style="display:none;flex:1;flex-direction:column;">
|
||||||
|
<!-- Blue ANSI bar -->
|
||||||
|
<div style="background:#2563eb;text-align:center;padding:6px 0;">
|
||||||
|
<span class="t-bold" style="color:#fff;font-family:'JetBrains Mono',monospace;font-size:13px;">General</span>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Chat messages area -->
|
||||||
|
<div id="chatMessages" style="flex:1;padding:12px 16px;display:flex;flex-direction:column;gap:2px;font-family:'JetBrains Mono',monospace;font-size:13px;line-height:1.7;min-height:240px;">
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Green separator line -->
|
||||||
|
<div id="greenLine" style="padding:0 16px;font-family:'JetBrains Mono',monospace;font-size:13px;color:#22c55e;user-select:none;opacity:0;transition:opacity 0.3s;overflow:hidden;">
|
||||||
|
<span style="display:block;width:100%;border-bottom:1px dashed #22c55e;line-height:0;height:0;"></span>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Input area -->
|
||||||
|
<div id="inputArea" style="padding:6px 16px 14px;font-family:'JetBrains Mono',monospace;font-size:13px;opacity:0;transition:opacity 0.3s;">
|
||||||
|
<span class="t-dim">></span> <span id="typingText"></span><span class="cursor-blink"></span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<!-- FEATURES -->
|
||||||
|
<section id="features">
|
||||||
|
<div class="container fade-in">
|
||||||
|
<div class="section-label">// features</div>
|
||||||
|
<h2 class="section-title">Everything you need</h2>
|
||||||
|
<p class="section-desc">Terminal Chat is built with a focus on simplicity and functionality. No unnecessary complexity, just chatting from your terminal.</p>
|
||||||
|
<div class="features-grid">
|
||||||
|
<div class="feature-card">
|
||||||
|
<div class="feature-icon">
|
||||||
|
<svg width="20" height="20" viewBox="0 0 20 20" fill="none" stroke="#22c55e" stroke-width="1.5"><rect x="2" y="3" width="16" height="14" rx="2"/><path d="M6 9l2 2 4-4"/></svg>
|
||||||
|
</div>
|
||||||
|
<h3>Login & Signup</h3>
|
||||||
|
<p>Full authentication system with username and password. Default test account included.</p>
|
||||||
|
</div>
|
||||||
|
<div class="feature-card">
|
||||||
|
<div class="feature-icon">
|
||||||
|
<svg width="20" height="20" viewBox="0 0 20 20" fill="none" stroke="#22c55e" stroke-width="1.5"><path d="M4 16V4l6 4-6 4"/><path d="M14 8h4"/></svg>
|
||||||
|
</div>
|
||||||
|
<h3>Terminal Interface</h3>
|
||||||
|
<p>Colorful terminal interface with intuitive navigation. Works on any terminal that supports ANSI colors.</p>
|
||||||
|
</div>
|
||||||
|
<div class="feature-card">
|
||||||
|
<div class="feature-icon">
|
||||||
|
<svg width="20" height="20" viewBox="0 0 20 20" fill="none" stroke="#22c55e" stroke-width="1.5"><circle cx="10" cy="10" r="7"/><path d="M10 6v4l2.5 2.5"/></svg>
|
||||||
|
</div>
|
||||||
|
<h3>Realtime Messaging</h3>
|
||||||
|
<p>Messages are delivered instantly via TCP sockets. No delay, no polling. True realtime communication.</p>
|
||||||
|
</div>
|
||||||
|
<div class="feature-card">
|
||||||
|
<div class="feature-icon">
|
||||||
|
<svg width="20" height="20" viewBox="0 0 20 20" fill="none" stroke="#22c55e" stroke-width="1.5"><path d="M3 5h14M3 10h14M3 15h14"/><circle cx="7" cy="5" r="1.5" fill="#22c55e"/><circle cx="13" cy="10" r="1.5" fill="#22c55e"/><circle cx="9" cy="15" r="1.5" fill="#22c55e"/></svg>
|
||||||
|
</div>
|
||||||
|
<h3>Multiple Chats</h3>
|
||||||
|
<p>Create multiple chat channels and easily switch between them. Each channel has its own message history.</p>
|
||||||
|
</div>
|
||||||
|
<div class="feature-card">
|
||||||
|
<div class="feature-icon">
|
||||||
|
<svg width="20" height="20" viewBox="0 0 20 20" fill="none" stroke="#22c55e" stroke-width="1.5"><circle cx="7" cy="8" r="3"/><circle cx="14" cy="8" r="3"/><path d="M2 16c0-2.2 2.2-4 5-4s5 1.8 5 4"/><path d="M12 12c2.8 0 5 1.8 5 4"/></svg>
|
||||||
|
</div>
|
||||||
|
<h3>User Management</h3>
|
||||||
|
<p>Add users to chats with a simple command. Manage who is in which channel.</p>
|
||||||
|
</div>
|
||||||
|
<div class="feature-card">
|
||||||
|
<div class="feature-icon">
|
||||||
|
<svg width="20" height="20" viewBox="0 0 20 20" fill="none" stroke="#22c55e" stroke-width="1.5"><path d="M10 2a8 8 0 100 16 8 8 0 000-16z"/><path d="M10 6v8M6 10h8"/></svg>
|
||||||
|
</div>
|
||||||
|
<h3>Open Source</h3>
|
||||||
|
<p>Fully open source and free to use. View the code, contribute, or fork the project for your own use.</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<section id="compatibility">
|
||||||
|
<div class="container fade-in">
|
||||||
|
<div class="section-label">// compatibility</div>
|
||||||
|
<h2 class="section-title">Platform support</h2>
|
||||||
|
<p class="section-desc">Built primarily for Linux terminals, with growing support for other platforms.</p>
|
||||||
|
|
||||||
|
<div style="display:grid;grid-template-columns:repeat(auto-fit,minmax(280px,1fr));gap:16px;">
|
||||||
|
|
||||||
|
<!-- Linux -->
|
||||||
|
<div class="feature-card" style="border-color:rgba(34,197,94,0.3);">
|
||||||
|
<div style="display:flex;align-items:center;justify-content:space-between;margin-bottom:16px;">
|
||||||
|
<div style="display:flex;align-items:center;gap:12px;">
|
||||||
|
<div class="feature-icon">
|
||||||
|
<svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="#22c55e" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"><path d="M12 2C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2z"/><path d="M8 14s1.5 2 4 2 4-2 4-2"/><circle cx="9" cy="10" r="1" fill="#22c55e"/><circle cx="15" cy="10" r="1" fill="#22c55e"/></svg>
|
||||||
|
</div>
|
||||||
|
<h3 style="font-size:16px;font-weight:600;">Linux</h3>
|
||||||
|
</div>
|
||||||
|
<span style="font-family:'JetBrains Mono',monospace;font-size:11px;padding:4px 10px;border-radius:999px;background:rgba(34,197,94,0.15);color:#22c55e;border:1px solid rgba(34,197,94,0.25);font-weight:600;">FULLY SUPPORTED</span>
|
||||||
|
</div>
|
||||||
|
<p style="font-size:14px;color:var(--text-dim);line-height:1.6;margin-bottom:16px;">First-class support. All features work out of the box including ANSI colors, readline, and autofill.</p>
|
||||||
|
<div style="display:flex;flex-direction:column;gap:8px;">
|
||||||
|
<div style="display:flex;align-items:center;gap:8px;font-size:13px;">
|
||||||
|
<svg width="14" height="14" viewBox="0 0 16 16" fill="none" stroke="#22c55e" stroke-width="2"><path d="M3 8.5l3.5 3.5L13 4.5"/></svg>
|
||||||
|
<span style="color:var(--text-dim);">Full ANSI color support</span>
|
||||||
|
</div>
|
||||||
|
<div style="display:flex;align-items:center;gap:8px;font-size:13px;">
|
||||||
|
<svg width="14" height="14" viewBox="0 0 16 16" fill="none" stroke="#22c55e" stroke-width="2"><path d="M3 8.5l3.5 3.5L13 4.5"/></svg>
|
||||||
|
<span style="color:var(--text-dim);">Readline & autofill</span>
|
||||||
|
</div>
|
||||||
|
<div style="display:flex;align-items:center;gap:8px;font-size:13px;">
|
||||||
|
<svg width="14" height="14" viewBox="0 0 16 16" fill="none" stroke="#22c55e" stroke-width="2"><path d="M3 8.5l3.5 3.5L13 4.5"/></svg>
|
||||||
|
<span style="color:var(--text-dim);">Tested & verified</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Windows -->
|
||||||
|
<div class="feature-card" style="border-color:rgba(234,179,8,0.3);">
|
||||||
|
<div style="display:flex;align-items:center;justify-content:space-between;margin-bottom:16px;">
|
||||||
|
<div style="display:flex;align-items:center;gap:12px;">
|
||||||
|
<div class="feature-icon" style="background:rgba(234,179,8,0.15);">
|
||||||
|
<svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="#eab308" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"><rect x="3" y="3" width="8" height="8" rx="1"/><rect x="13" y="3" width="8" height="8" rx="1"/><rect x="3" y="13" width="8" height="8" rx="1"/><rect x="13" y="13" width="8" height="8" rx="1"/></svg>
|
||||||
|
</div>
|
||||||
|
<h3 style="font-size:16px;font-weight:600;">Windows</h3>
|
||||||
|
</div>
|
||||||
|
<span style="font-family:'JetBrains Mono',monospace;font-size:11px;padding:4px 10px;border-radius:999px;background:rgba(234,179,8,0.15);color:#eab308;border:1px solid rgba(234,179,8,0.25);font-weight:600;">PARTIAL</span>
|
||||||
|
</div>
|
||||||
|
<p style="font-size:14px;color:var(--text-dim);line-height:1.6;margin-bottom:16px;">Works with modifications. The readline module and autofill function need to be adjusted for Windows compatibility.</p>
|
||||||
|
<div style="display:flex;flex-direction:column;gap:8px;">
|
||||||
|
<div style="display:flex;align-items:center;gap:8px;font-size:13px;">
|
||||||
|
<svg width="14" height="14" viewBox="0 0 16 16" fill="none" stroke="#22c55e" stroke-width="2"><path d="M3 8.5l3.5 3.5L13 4.5"/></svg>
|
||||||
|
<span style="color:var(--text-dim);">Core chat functionality</span>
|
||||||
|
</div>
|
||||||
|
<div style="display:flex;align-items:center;gap:8px;font-size:13px;">
|
||||||
|
<svg width="14" height="14" viewBox="0 0 16 16" fill="none" stroke="#eab308" stroke-width="2"><path d="M8 3v6M8 12v1"/></svg>
|
||||||
|
<span style="color:var(--text-dim);">Readline needs <a href="https://ben.de-roo.org/install/Windows" class="green-link" style="color:#eab308;">pyreadline3</a></span>
|
||||||
|
</div>
|
||||||
|
<div style="display:flex;align-items:center;gap:8px;font-size:13px;">
|
||||||
|
<svg width="14" height="14" viewBox="0 0 16 16" fill="none" stroke="#eab308" stroke-width="2"><path d="M8 3v6M8 12v1"/></svg>
|
||||||
|
<span style="color:var(--text-dim);">Autofill needs adjustment</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- macOS -->
|
||||||
|
<div class="feature-card" style="border-color:rgba(100,116,139,0.3);">
|
||||||
|
<div style="display:flex;align-items:center;justify-content:space-between;margin-bottom:16px;">
|
||||||
|
<div style="display:flex;align-items:center;gap:12px;">
|
||||||
|
<div class="feature-icon" style="background:rgba(100,116,139,0.15);">
|
||||||
|
<svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="#94a3b8" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"><path d="M12 2C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2z"/><path d="M9 9h6v6H9z"/></svg>
|
||||||
|
</div>
|
||||||
|
<h3 style="font-size:16px;font-weight:600;">macOS</h3>
|
||||||
|
</div>
|
||||||
|
<span style="font-family:'JetBrains Mono',monospace;font-size:11px;padding:4px 10px;border-radius:999px;background:rgba(100,116,139,0.15);color:#94a3b8;border:1px solid rgba(100,116,139,0.25);font-weight:600;">UNTESTED</span>
|
||||||
|
</div>
|
||||||
|
<p style="font-size:14px;color:var(--text-dim);line-height:1.6;margin-bottom:16px;">Should work since macOS has a Unix-based terminal, but it hasn't been tested yet. Community feedback is welcome!</p>
|
||||||
|
<div style="display:flex;flex-direction:column;gap:8px;">
|
||||||
|
<div style="display:flex;align-items:center;gap:8px;font-size:13px;">
|
||||||
|
<svg width="14" height="14" viewBox="0 0 16 16" fill="none" stroke="#64748b" stroke-width="2"><path d="M8 3v6M8 12v1"/></svg>
|
||||||
|
<span style="color:var(--text-dim);">Likely compatible (Unix-based)</span>
|
||||||
|
</div>
|
||||||
|
<div style="display:flex;align-items:center;gap:8px;font-size:13px;">
|
||||||
|
<svg width="14" height="14" viewBox="0 0 16 16" fill="none" stroke="#64748b" stroke-width="2"><path d="M8 3v6M8 12v1"/></svg>
|
||||||
|
<span style="color:var(--text-dim);">Needs community testing</span>
|
||||||
|
</div>
|
||||||
|
<div style="display:flex;align-items:center;gap:8px;font-size:13px;">
|
||||||
|
<svg width="14" height="14" viewBox="0 0 16 16" fill="none" stroke="#64748b" stroke-width="2"><path d="M8 3v6M8 12v1"/></svg>
|
||||||
|
<span style="color:var(--text-dim);">Open an issue to report results</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
|
||||||
|
<!-- DOCUMENTATIE -->
|
||||||
|
<section id="docs">
|
||||||
|
<div class="container fade-in">
|
||||||
|
<div class="section-label">// documentation</div>
|
||||||
|
<h2 class="section-title">Get started quickly</h2>
|
||||||
|
<p class="section-desc">Everything you need to install, configure, and use Terminal Chat.</p>
|
||||||
|
<div class="docs-grid">
|
||||||
|
|
||||||
|
<!-- Installation -->
|
||||||
|
<div class="doc-card">
|
||||||
|
<div class="doc-card-header">
|
||||||
|
<svg width="20" height="20" viewBox="0 0 20 20" fill="none" stroke="#22c55e" stroke-width="1.5"><path d="M10 2v12m0 0l-4-4m4 4l4-4M3 17h14"/></svg>
|
||||||
|
<h3>Installation (for Linux)</h3>
|
||||||
|
</div>
|
||||||
|
<div class="doc-card-body">
|
||||||
|
<p>Install Terminal Chat with a single command. The script downloads and configures everything automatically.</p>
|
||||||
|
<a href="https://ben.de-roo.org/install/Windows" class="green-link">Windows installation and setup</a>
|
||||||
|
<br><br>
|
||||||
|
<div class="code-block">
|
||||||
|
<span class="t-dim"># Automatic installation</span><br>
|
||||||
|
<span class="t-green">$</span> curl -s https://ben.de-roo.org/install/script.sh | bash<br><br>
|
||||||
|
<span class="t-dim"># Or manually</span><br>
|
||||||
|
<span class="t-green">$</span> git clone https://git.de-roo.org/ben/pychat.git<br>
|
||||||
|
<span class="t-green">$</span> cd terminal-chat<br>
|
||||||
|
<span class="t-green">$</span> pip install -r requirements.txt
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Getting Started -->
|
||||||
|
<div class="doc-card">
|
||||||
|
<div class="doc-card-header">
|
||||||
|
<svg width="20" height="20" viewBox="0 0 20 20" fill="none" stroke="#06b6d4" stroke-width="1.5"><path d="M7 4l8 6-8 6V4z"/></svg>
|
||||||
|
<h3>Getting Started</h3>
|
||||||
|
</div>
|
||||||
|
<div class="doc-card-body">
|
||||||
|
<p>Start the server first, then the client. Log in with the default test account or create a new one.</p>
|
||||||
|
<div class="code-block">
|
||||||
|
<span class="t-dim"># Start the server</span><br>
|
||||||
|
<span class="t-green">$</span> python3 server.py<br><br>
|
||||||
|
<span class="t-dim"># Start the client (another terminal window)</span><br>
|
||||||
|
<span class="t-green">$</span> python3 client.py<br><br>
|
||||||
|
<span class="t-dim"># Default test account</span><br>
|
||||||
|
<span class="t-yellow">user:</span> test <span class="t-yellow">pass:</span> test
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Commands -->
|
||||||
|
<div class="doc-card" style="grid-column: 1 / -1;">
|
||||||
|
<div class="doc-card-header">
|
||||||
|
<svg width="20" height="20" viewBox="0 0 20 20" fill="none" stroke="#eab308" stroke-width="1.5"><path d="M4 16V4l6 4-6 4"/><path d="M14 8h4"/></svg>
|
||||||
|
<h3>Available Commands</h3>
|
||||||
|
</div>
|
||||||
|
<div class="doc-card-body">
|
||||||
|
<p>Use these commands while chatting to perform additional actions.</p>
|
||||||
|
<table class="command-table">
|
||||||
|
<thead>
|
||||||
|
<tr><th>Command</th><th>Description</th></tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
<tr>
|
||||||
|
<td>/add <username></td>
|
||||||
|
<td>Add a user to the current chat</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>/break</td>
|
||||||
|
<td>Leave the current chat and return to chat selection</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>/quit</td>
|
||||||
|
<td>Close the program completely and disconnect</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>obviously more commands and features comming on the way!</td>
|
||||||
|
</tr>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<!-- ARCHITECTUUR -->
|
||||||
|
<section id="architectuur">
|
||||||
|
<div class="container fade-in">
|
||||||
|
<div class="section-label">// architecture</div>
|
||||||
|
<h2 class="section-title">How it works</h2>
|
||||||
|
<p class="section-desc">Terminal Chat uses a classic client-server model with TCP sockets for reliable, bidirectional communication.</p>
|
||||||
|
<div class="arch-block">
|
||||||
|
<div class="arch-row">
|
||||||
|
<span class="arch-label">Client</span>
|
||||||
|
<span class="arch-value">Python script that connects to the server. Handles input/output and displays messages with ANSI colors.</span>
|
||||||
|
</div>
|
||||||
|
<div class="arch-arrow">↓ TCP Socket ↓</div>
|
||||||
|
<div class="arch-row">
|
||||||
|
<span class="arch-label">Server</span>
|
||||||
|
<span class="arch-value">Runs centrally and manages all connections, authentication, chat channels, and message routing.</span>
|
||||||
|
</div>
|
||||||
|
<div class="arch-arrow">↓ Threading ↓</div>
|
||||||
|
<div class="arch-row">
|
||||||
|
<span class="arch-label">Storage</span>
|
||||||
|
<span class="arch-value">User and chat data are stored server-side. Each client gets its own thread for non-blocking I/O.</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<br><br>
|
||||||
|
<div class="code-block" style="max-width:700px;">
|
||||||
|
<span class="t-dim"># Simplified overview</span><br><br>
|
||||||
|
<span class="t-cyan">client.py</span> <span class="t-dim">→</span> Connects to server via socket<br>
|
||||||
|
<span class="t-dim">→</span> Login / Signup prompt<br>
|
||||||
|
<span class="t-dim">→</span> Chat selection menu<br>
|
||||||
|
<span class="t-dim">→</span> Send & receive messages<br><br>
|
||||||
|
<span class="t-cyan">server.py</span> <span class="t-dim">→</span> Listens on TCP port<br>
|
||||||
|
<span class="t-dim">→</span> New thread per client<br>
|
||||||
|
<span class="t-dim">→</span> Authentication handling<br>
|
||||||
|
<span class="t-dim">→</span> Message broadcast to channel
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<!-- CONTRIBUTE / JOIN THE PROJECT -->
|
||||||
|
<section id="contribute">
|
||||||
|
<div class="container fade-in">
|
||||||
|
<div class="section-label">// contribute</div>
|
||||||
|
<h2 class="section-title">Make it yours</h2>
|
||||||
|
<p class="section-desc">Terminal Chat is open source and built to be extended. Whether you want to add new features, build mods, or fix bugs — you're welcome to join.</p>
|
||||||
|
|
||||||
|
<div class="contribute-grid">
|
||||||
|
|
||||||
|
<div class="contribute-card">
|
||||||
|
<div class="card-number">01</div>
|
||||||
|
<div style="display:flex;align-items:center;gap:10px;margin-bottom:12px;">
|
||||||
|
<div class="feature-icon">
|
||||||
|
<svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="#22c55e" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"><path d="M12 5v14M5 12h14"/></svg>
|
||||||
|
</div>
|
||||||
|
<h3 style="font-size:16px;font-weight:600;">Build mods</h3>
|
||||||
|
</div>
|
||||||
|
<p style="font-size:14px;color:var(--text-dim);line-height:1.6;">Add new commands, custom themes, message encryption, file sharing, or anything else you can think of. The codebase is simple and easy to extend.</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="contribute-card">
|
||||||
|
<div class="card-number">02</div>
|
||||||
|
<div style="display:flex;align-items:center;gap:10px;margin-bottom:12px;">
|
||||||
|
<div class="feature-icon" style="background:rgba(6,182,212,0.15);">
|
||||||
|
<svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="#06b6d4" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"><path d="M16 18l6-6-6-6"/><path d="M8 6l-6 6 6 6"/></svg>
|
||||||
|
</div>
|
||||||
|
<h3 style="font-size:16px;font-weight:600;">Improve the core</h3>
|
||||||
|
</div>
|
||||||
|
<p style="font-size:14px;color:var(--text-dim);line-height:1.6;">Help fix bugs, improve Windows/macOS compatibility, optimize performance, or refactor the codebase. Every PR matters.</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="contribute-card">
|
||||||
|
<div class="card-number">03</div>
|
||||||
|
<div style="display:flex;align-items:center;gap:10px;margin-bottom:12px;">
|
||||||
|
<div class="feature-icon" style="background:rgba(234,179,8,0.15);">
|
||||||
|
<svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="#eab308" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"><path d="M21 15a2 2 0 01-2 2H7l-4 4V5a2 2 0 012-2h14a2 2 0 012 2z"/></svg>
|
||||||
|
</div>
|
||||||
|
<h3 style="font-size:16px;font-weight:600;">Share ideas</h3>
|
||||||
|
</div>
|
||||||
|
<p style="font-size:14px;color:var(--text-dim);line-height:1.6;">Open an issue to suggest features, report bugs, or discuss improvements. Even if you don't code, your input helps shape the project.</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="contribute-card">
|
||||||
|
<div class="card-number">04</div>
|
||||||
|
<div style="display:flex;align-items:center;gap:10px;margin-bottom:12px;">
|
||||||
|
<div class="feature-icon" style="background:rgba(249,115,22,0.15);">
|
||||||
|
<svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="#f97316" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"><path d="M9 19c-5 1.5-5-2.5-7-3m14 6v-3.87a3.37 3.37 0 00-.94-2.61c3.14-.35 6.44-1.54 6.44-7A5.44 5.44 0 0020 4.77 5.07 5.07 0 0019.91 1S18.73.65 16 2.48a13.38 13.38 0 00-7 0C6.27.65 5.09 1 5.09 1A5.07 5.07 0 005 4.77a5.44 5.44 0 00-1.5 3.78c0 5.42 3.3 6.61 6.44 7A3.37 3.37 0 009 18.13V22"/></svg>
|
||||||
|
</div>
|
||||||
|
<h3 style="font-size:16px;font-weight:600;">Fork & experiment</h3>
|
||||||
|
</div>
|
||||||
|
<p style="font-size:14px;color:var(--text-dim);line-height:1.6;">Fork the repo and go wild. Build your own version, add a GUI, make it work with Discord bots — the code is yours to play with.</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- CTA -->
|
||||||
|
<div style="margin-top:48px;text-align:center;">
|
||||||
|
<p style="font-family:'JetBrains Mono',monospace;font-size:14px;color:var(--text-dim);margin-bottom:20px;">Ready to contribute? Start here:</p>
|
||||||
|
<div class="code-block" style="max-width:500px;margin:0 auto 24px;text-align:left;">
|
||||||
|
<span class="t-dim"># Clone and start hacking</span><br>
|
||||||
|
<span class="t-green">$</span> git clone https://git.de-roo.org/ben/pychat.git<br>
|
||||||
|
<span class="t-green">$</span> cd pychat<br>
|
||||||
|
<span class="t-green">$</span> python3 server.py <span class="t-dim">&</span> python3 client.py
|
||||||
|
</div>
|
||||||
|
<p style="font-size:13px;color:var(--text-muted);">No complex setup. No build tools. Just Python.</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<!-- UPDATES / CHANGELOG -->
|
||||||
|
<section id="updates">
|
||||||
|
<div class="container fade-in">
|
||||||
|
<div class="section-label">// changelog</div>
|
||||||
|
<h2 class="section-title">Updates & News</h2>
|
||||||
|
<p class="section-desc">Latest releases, patches, and project news.</p>
|
||||||
|
<div class="updates-list" id="updatesList">
|
||||||
|
<div class="updates-empty">Loading updates...</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<!-- FOOTER -->
|
||||||
|
<footer>
|
||||||
|
<p>Terminal Chat — Built with Python & TCP Sockets</p>
|
||||||
|
<p style="margin-top: 8px;">
|
||||||
|
<a href="#demo">Demo</a> ·
|
||||||
|
<a href="#docs">Documentation</a> ·
|
||||||
|
<a href="#features">Features</a> ·
|
||||||
|
<a href="#contribute">Contribute</a> ·
|
||||||
|
<a href="#updates">Updates</a> ·
|
||||||
|
<a href="https://git.de-roo.org/ben/pychat" target="_blank" rel="noopener noreferrer">Source Code</a>
|
||||||
|
</p>
|
||||||
|
</footer>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
// === NAV SCROLL ===
|
||||||
|
const navbar = document.getElementById('navbar');
|
||||||
|
window.addEventListener('scroll', () => {
|
||||||
|
navbar.classList.toggle('scrolled', window.scrollY > 20);
|
||||||
|
});
|
||||||
|
|
||||||
|
// === COPY INSTALL COMMAND ===
|
||||||
|
function copyInstall() {
|
||||||
|
const cmd = document.getElementById('installCmd').textContent;
|
||||||
|
navigator.clipboard.writeText(cmd).then(() => {
|
||||||
|
const copyIcon = document.getElementById('copyIcon');
|
||||||
|
const checkIcon = document.getElementById('checkIcon');
|
||||||
|
const btn = document.getElementById('copyBtn');
|
||||||
|
copyIcon.style.display = 'none';
|
||||||
|
checkIcon.style.display = 'block';
|
||||||
|
btn.classList.add('copied');
|
||||||
|
setTimeout(() => {
|
||||||
|
copyIcon.style.display = 'block';
|
||||||
|
checkIcon.style.display = 'none';
|
||||||
|
btn.classList.remove('copied');
|
||||||
|
}, 2000);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// === TERMINAL ANIMATION ===
|
||||||
|
const chatMessages = [
|
||||||
|
{ user: 'alice', color: '#06b6d4', text: 'hey everyone, server is up', align: 'left' },
|
||||||
|
{ user: 'jake', color: '#f97316', text: 'nice, just connected', align: 'left' },
|
||||||
|
{ user: 'alice', color: '#06b6d4', text: 'max you there?', align: 'left' },
|
||||||
|
{ user: 'max', color: '#eab308', text: 'yeah just logged in', align: 'right' },
|
||||||
|
{ user: 'jake', color: '#f97316', text: 'this thing is actually fast', align: 'left' },
|
||||||
|
{ user: 'max', color: '#eab308', text: 'right? tcp sockets go crazy', align: 'right' },
|
||||||
|
{ user: 'alice', color: '#06b6d4', text: 'lol true. let me add sarah', align: 'left' },
|
||||||
|
{ user: 'alice', color: '#06b6d4', text: '/add sarah', align: 'left', isCmd: true },
|
||||||
|
{ user: null, color: '#22c55e', text: 'sarah has been added to the chat.', align: 'center', isSystem: true },
|
||||||
|
{ user: 'sarah', color: '#a78bfa', text: 'heyy thanks for the invite', align: 'left' },
|
||||||
|
{ user: 'max', color: '#eab308', text: 'welcome! we were just testing', align: 'right' },
|
||||||
|
{ user: 'sarah', color: '#a78bfa', text: 'looks super clean ngl', align: 'left' },
|
||||||
|
{ user: 'jake', color: '#f97316', text: 'agreed, terminal chat supremacy', align: 'left' },
|
||||||
|
{ user: 'max', color: '#eab308', text: 'haha lets gooo', align: 'right' },
|
||||||
|
];
|
||||||
|
|
||||||
|
let typingMsg = 'who else should we add?';
|
||||||
|
|
||||||
|
function animateTerminal() {
|
||||||
|
const termBody = document.getElementById('terminalBody');
|
||||||
|
const observer = new IntersectionObserver((entries) => {
|
||||||
|
entries.forEach(entry => {
|
||||||
|
if (entry.isIntersecting) {
|
||||||
|
startLoginPhase();
|
||||||
|
observer.unobserve(entry.target);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}, { threshold: 0.3 });
|
||||||
|
observer.observe(termBody);
|
||||||
|
}
|
||||||
|
|
||||||
|
function startLoginPhase() {
|
||||||
|
const lines = document.querySelectorAll('#loginPhase .terminal-line');
|
||||||
|
lines.forEach(line => {
|
||||||
|
const delay = parseInt(line.getAttribute('data-delay')) || 0;
|
||||||
|
setTimeout(() => line.classList.add('visible'), delay);
|
||||||
|
});
|
||||||
|
|
||||||
|
// After login flow finishes, transition to chat
|
||||||
|
const lastDelay = Math.max(...Array.from(lines).map(l => parseInt(l.getAttribute('data-delay')) || 0));
|
||||||
|
setTimeout(() => {
|
||||||
|
transitionToChat();
|
||||||
|
}, lastDelay + 800);
|
||||||
|
}
|
||||||
|
|
||||||
|
function transitionToChat() {
|
||||||
|
const loginPhase = document.getElementById('loginPhase');
|
||||||
|
const chatPhase = document.getElementById('chatPhase');
|
||||||
|
|
||||||
|
loginPhase.style.transition = 'opacity 0.4s';
|
||||||
|
loginPhase.style.opacity = '0';
|
||||||
|
|
||||||
|
setTimeout(() => {
|
||||||
|
loginPhase.style.display = 'none';
|
||||||
|
chatPhase.style.display = 'flex';
|
||||||
|
|
||||||
|
// Show green line and input area
|
||||||
|
setTimeout(() => {
|
||||||
|
document.getElementById('greenLine').style.opacity = '1';
|
||||||
|
document.getElementById('inputArea').style.opacity = '1';
|
||||||
|
}, 300);
|
||||||
|
|
||||||
|
// Start adding messages one by one
|
||||||
|
let msgIndex = 0;
|
||||||
|
const msgContainer = document.getElementById('chatMessages');
|
||||||
|
const typingEl = document.getElementById('typingText');
|
||||||
|
|
||||||
|
function typeInInput(text, callback) {
|
||||||
|
typingEl.textContent = '';
|
||||||
|
let i = 0;
|
||||||
|
function typeChar() {
|
||||||
|
if (i < text.length) {
|
||||||
|
typingEl.textContent += text[i];
|
||||||
|
i++;
|
||||||
|
setTimeout(typeChar, 45 + Math.random() * 60);
|
||||||
|
} else {
|
||||||
|
// Brief pause then "send"
|
||||||
|
setTimeout(() => {
|
||||||
|
typingEl.textContent = '';
|
||||||
|
callback();
|
||||||
|
}, 350);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
typeChar();
|
||||||
|
}
|
||||||
|
|
||||||
|
function appendMessage(msg) {
|
||||||
|
const div = document.createElement('div');
|
||||||
|
div.style.opacity = '0';
|
||||||
|
div.style.transform = 'translateY(4px)';
|
||||||
|
div.style.transition = 'all 0.3s ease';
|
||||||
|
|
||||||
|
if (msg.isSystem) {
|
||||||
|
div.style.textAlign = 'center';
|
||||||
|
div.innerHTML = '<span style="color:' + msg.color + ';font-size:12px;">' + msg.text + '</span>';
|
||||||
|
} else if (msg.align === 'right') {
|
||||||
|
div.style.textAlign = 'right';
|
||||||
|
div.innerHTML = '<span style="color:#64748b;">[</span><span style="color:' + msg.color + ';">' + msg.user + '</span><span style="color:#64748b;">]</span> ' + (msg.isCmd ? '<span style="color:#f97316;">' + msg.text + '</span>' : msg.text);
|
||||||
|
} else {
|
||||||
|
div.style.textAlign = 'left';
|
||||||
|
div.innerHTML = '<span style="color:#64748b;">[</span><span style="color:' + msg.color + ';">' + msg.user + '</span><span style="color:#64748b;">]</span> ' + (msg.isCmd ? '<span style="color:#f97316;">' + msg.text + '</span>' : msg.text);
|
||||||
|
}
|
||||||
|
|
||||||
|
msgContainer.appendChild(div);
|
||||||
|
requestAnimationFrame(() => {
|
||||||
|
requestAnimationFrame(() => {
|
||||||
|
div.style.opacity = '1';
|
||||||
|
div.style.transform = 'translateY(0)';
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function addNextMessage() {
|
||||||
|
if (msgIndex >= chatMessages.length) {
|
||||||
|
// Final typing animation (no send)
|
||||||
|
setTimeout(() => {
|
||||||
|
let i = 0;
|
||||||
|
function lastType() {
|
||||||
|
if (i < typingMsg.length) {
|
||||||
|
typingEl.textContent += typingMsg[i];
|
||||||
|
i++;
|
||||||
|
setTimeout(lastType, 60 + Math.random() * 80);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
lastType();
|
||||||
|
}, 400);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const msg = chatMessages[msgIndex];
|
||||||
|
msgIndex++;
|
||||||
|
|
||||||
|
if (msg.user === 'max') {
|
||||||
|
// Max's messages: type in input first, then send to chat
|
||||||
|
typeInInput(msg.text, () => {
|
||||||
|
appendMessage(msg);
|
||||||
|
setTimeout(addNextMessage, 400 + Math.random() * 400);
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
// Other users: just appear after a delay
|
||||||
|
appendMessage(msg);
|
||||||
|
setTimeout(addNextMessage, 600 + Math.random() * 500);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
setTimeout(addNextMessage, 500);
|
||||||
|
}, 400);
|
||||||
|
}
|
||||||
|
|
||||||
|
animateTerminal();
|
||||||
|
|
||||||
|
// === FADE-IN SECTIONS ===
|
||||||
|
const fadeObserver = new IntersectionObserver((entries) => {
|
||||||
|
entries.forEach(entry => {
|
||||||
|
if (entry.isIntersecting) {
|
||||||
|
entry.target.classList.add('visible');
|
||||||
|
fadeObserver.unobserve(entry.target);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}, { threshold: 0.15 });
|
||||||
|
|
||||||
|
document.querySelectorAll('.fade-in').forEach(el => fadeObserver.observe(el));
|
||||||
|
|
||||||
|
// === INTERACTIVE RED DOTS (close & reboot terminals) ===
|
||||||
|
function setupRedDots() {
|
||||||
|
// Main terminal red dot
|
||||||
|
const terminalDots = document.querySelectorAll('.terminal-bar-dots');
|
||||||
|
terminalDots.forEach(dotsContainer => {
|
||||||
|
const redDot = dotsContainer.querySelector('span:first-child');
|
||||||
|
const termWindow = dotsContainer.closest('.terminal-window');
|
||||||
|
if (!redDot || !termWindow) return;
|
||||||
|
|
||||||
|
redDot.title = 'Close terminal';
|
||||||
|
redDot.addEventListener('click', () => {
|
||||||
|
if (termWindow.classList.contains('terminal-closing') || termWindow.classList.contains('terminal-opening')) return;
|
||||||
|
|
||||||
|
termWindow.classList.add('terminal-closing');
|
||||||
|
termWindow.addEventListener('animationend', function onClose() {
|
||||||
|
termWindow.removeEventListener('animationend', onClose);
|
||||||
|
termWindow.style.visibility = 'hidden';
|
||||||
|
termWindow.classList.remove('terminal-closing');
|
||||||
|
|
||||||
|
// After 2 seconds, reopen and restart animation
|
||||||
|
setTimeout(() => {
|
||||||
|
// Reset the terminal contents
|
||||||
|
const loginPhase = document.getElementById('loginPhase');
|
||||||
|
const chatPhase = document.getElementById('chatPhase');
|
||||||
|
const chatMessages = document.getElementById('chatMessages');
|
||||||
|
const greenLine = document.getElementById('greenLine');
|
||||||
|
const inputArea = document.getElementById('inputArea');
|
||||||
|
const typingText = document.getElementById('typingText');
|
||||||
|
|
||||||
|
// Reset phases
|
||||||
|
loginPhase.style.display = 'block';
|
||||||
|
loginPhase.style.opacity = '1';
|
||||||
|
chatPhase.style.display = 'none';
|
||||||
|
chatMessages.innerHTML = '';
|
||||||
|
greenLine.style.opacity = '0';
|
||||||
|
inputArea.style.opacity = '0';
|
||||||
|
typingText.textContent = '';
|
||||||
|
|
||||||
|
// Reset all login lines
|
||||||
|
document.querySelectorAll('#loginPhase .terminal-line').forEach(line => {
|
||||||
|
line.classList.remove('visible');
|
||||||
|
});
|
||||||
|
|
||||||
|
termWindow.style.visibility = 'visible';
|
||||||
|
termWindow.classList.add('terminal-opening');
|
||||||
|
termWindow.addEventListener('animationend', function onOpen() {
|
||||||
|
termWindow.removeEventListener('animationend', onOpen);
|
||||||
|
termWindow.classList.remove('terminal-opening');
|
||||||
|
// Restart the login animation
|
||||||
|
startLoginPhase();
|
||||||
|
});
|
||||||
|
}, 2000);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
// Install box red dot
|
||||||
|
const installDots = document.querySelectorAll('.install-dots');
|
||||||
|
installDots.forEach(dotsContainer => {
|
||||||
|
const redDot = dotsContainer.querySelector('span:first-child');
|
||||||
|
const installBox = dotsContainer.closest('.install-box');
|
||||||
|
if (!redDot || !installBox) return;
|
||||||
|
|
||||||
|
redDot.title = 'Close terminal';
|
||||||
|
redDot.addEventListener('click', () => {
|
||||||
|
if (installBox.classList.contains('terminal-closing') || installBox.classList.contains('terminal-opening')) return;
|
||||||
|
|
||||||
|
installBox.classList.add('terminal-closing');
|
||||||
|
installBox.addEventListener('animationend', function onClose() {
|
||||||
|
installBox.removeEventListener('animationend', onClose);
|
||||||
|
installBox.style.visibility = 'hidden';
|
||||||
|
installBox.classList.remove('terminal-closing');
|
||||||
|
|
||||||
|
setTimeout(() => {
|
||||||
|
installBox.style.visibility = 'visible';
|
||||||
|
installBox.classList.add('terminal-opening');
|
||||||
|
installBox.addEventListener('animationend', function onOpen() {
|
||||||
|
installBox.removeEventListener('animationend', onOpen);
|
||||||
|
installBox.classList.remove('terminal-opening');
|
||||||
|
});
|
||||||
|
}, 2000);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
setupRedDots();
|
||||||
|
|
||||||
|
// === FLOATING PARTICLES ===
|
||||||
|
(function createParticles() {
|
||||||
|
const container = document.getElementById('particles');
|
||||||
|
if (!container) return;
|
||||||
|
for (let i = 0; i < 20; i++) {
|
||||||
|
const p = document.createElement('div');
|
||||||
|
p.classList.add('particle');
|
||||||
|
p.style.left = Math.random() * 100 + '%';
|
||||||
|
p.style.animationDuration = (12 + Math.random() * 18) + 's';
|
||||||
|
p.style.animationDelay = (Math.random() * 15) + 's';
|
||||||
|
p.style.width = p.style.height = (1.5 + Math.random() * 2) + 'px';
|
||||||
|
container.appendChild(p);
|
||||||
|
}
|
||||||
|
})();
|
||||||
|
|
||||||
|
// === CURSOR GLOW FOLLOWER ===
|
||||||
|
(function cursorGlow() {
|
||||||
|
const glow = document.getElementById('cursorGlow');
|
||||||
|
if (!glow) return;
|
||||||
|
document.addEventListener('mousemove', (e) => {
|
||||||
|
glow.style.left = e.clientX + 'px';
|
||||||
|
glow.style.top = e.clientY + 'px';
|
||||||
|
});
|
||||||
|
})();
|
||||||
|
|
||||||
|
// === LOAD UPDATES FROM JSON ===
|
||||||
|
(async function loadUpdates() {
|
||||||
|
const container = document.getElementById('updatesList');
|
||||||
|
try {
|
||||||
|
const res = await fetch('./updates.json?v=' + Date.now());
|
||||||
|
if (!res.ok) throw new Error('Failed to load');
|
||||||
|
const updates = await res.json();
|
||||||
|
|
||||||
|
if (!updates.length) {
|
||||||
|
container.innerHTML = '<div class="updates-empty">No updates yet. Check back soon.</div>';
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
container.innerHTML = updates.map(item => `
|
||||||
|
<div class="update-item">
|
||||||
|
<div class="update-dot"></div>
|
||||||
|
<div class="update-meta">
|
||||||
|
<span class="update-badge" data-type="${item.type}">${item.type}</span>
|
||||||
|
${item.version ? `<span class="update-version">${item.version}</span>` : ''}
|
||||||
|
<span class="update-date">${new Date(item.date).toLocaleDateString('en-US', { month: 'short', day: 'numeric', year: 'numeric' })}</span>
|
||||||
|
</div>
|
||||||
|
<div class="update-title">${item.title}</div>
|
||||||
|
<div class="update-desc">${item.description}</div>
|
||||||
|
</div>
|
||||||
|
`).join('');
|
||||||
|
} catch (e) {
|
||||||
|
container.innerHTML = '<div class="updates-empty">Could not load updates.</div>';
|
||||||
|
}
|
||||||
|
})();
|
||||||
|
</script>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
Reference in New Issue
Block a user