Checkpoint from VS Code for coding agent session
This commit is contained in:
parent
ee8f78be8c
commit
e6ab906dc0
39 changed files with 3572 additions and 64 deletions
784
aethex-connect-mockup.html
Normal file
784
aethex-connect-mockup.html
Normal file
|
|
@ -0,0 +1,784 @@
|
|||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>AeThex Connect - Metaverse Communication</title>
|
||||
<style>
|
||||
@import url('https://fonts.googleapis.com/css2?family=Roboto+Mono:wght@300;400;500;700&display=swap');
|
||||
|
||||
* {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
body {
|
||||
font-family: 'Roboto Mono', monospace;
|
||||
background: #0a0a0a;
|
||||
color: #e0e0e0;
|
||||
overflow: hidden;
|
||||
height: 100vh;
|
||||
}
|
||||
|
||||
/* Scanline effect */
|
||||
body::before {
|
||||
content: '';
|
||||
position: fixed;
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
background: repeating-linear-gradient(
|
||||
0deg,
|
||||
rgba(0, 0, 0, 0.15),
|
||||
rgba(0, 0, 0, 0.15) 1px,
|
||||
transparent 1px,
|
||||
transparent 2px
|
||||
);
|
||||
pointer-events: none;
|
||||
z-index: 1000;
|
||||
}
|
||||
|
||||
/* Main Layout */
|
||||
.connect-container {
|
||||
display: flex;
|
||||
height: 100vh;
|
||||
}
|
||||
|
||||
/* Server Sidebar */
|
||||
.server-list {
|
||||
width: 80px;
|
||||
background: #0d0d0d;
|
||||
border-right: 1px solid #1a1a1a;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
padding: 12px 0;
|
||||
gap: 12px;
|
||||
}
|
||||
|
||||
.server-icon {
|
||||
width: 56px;
|
||||
height: 56px;
|
||||
background: #1a1a1a;
|
||||
border-radius: 16px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
font-weight: 700;
|
||||
font-size: 1.2em;
|
||||
cursor: pointer;
|
||||
transition: all 0.3s;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.server-icon:hover {
|
||||
border-radius: 12px;
|
||||
transform: translateY(-2px);
|
||||
}
|
||||
|
||||
.server-icon.active {
|
||||
border-radius: 12px;
|
||||
}
|
||||
|
||||
.server-icon::before {
|
||||
content: '';
|
||||
position: absolute;
|
||||
left: -12px;
|
||||
width: 4px;
|
||||
height: 0;
|
||||
transition: height 0.3s;
|
||||
border-radius: 0 4px 4px 0;
|
||||
}
|
||||
|
||||
.server-icon.active::before {
|
||||
height: 40px;
|
||||
}
|
||||
|
||||
.server-icon.foundation {
|
||||
background: linear-gradient(135deg, #ff0000 0%, #990000 100%);
|
||||
}
|
||||
|
||||
.server-icon.foundation.active::before {
|
||||
background: #ff0000;
|
||||
}
|
||||
|
||||
.server-icon.corporation {
|
||||
background: linear-gradient(135deg, #0066ff 0%, #003380 100%);
|
||||
}
|
||||
|
||||
.server-icon.corporation.active::before {
|
||||
background: #0066ff;
|
||||
}
|
||||
|
||||
.server-icon.labs {
|
||||
background: linear-gradient(135deg, #ffa500 0%, #ff8c00 100%);
|
||||
}
|
||||
|
||||
.server-icon.labs.active::before {
|
||||
background: #ffa500;
|
||||
}
|
||||
|
||||
.server-icon.community {
|
||||
background: #1a1a1a;
|
||||
color: #666;
|
||||
}
|
||||
|
||||
.server-divider {
|
||||
width: 40px;
|
||||
height: 2px;
|
||||
background: #1a1a1a;
|
||||
margin: 4px 0;
|
||||
}
|
||||
|
||||
/* Channel Sidebar */
|
||||
.channel-sidebar {
|
||||
width: 280px;
|
||||
background: #0f0f0f;
|
||||
border-right: 1px solid #1a1a1a;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.server-header {
|
||||
padding: 16px;
|
||||
border-bottom: 1px solid #1a1a1a;
|
||||
font-weight: 700;
|
||||
font-size: 1.1em;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
}
|
||||
|
||||
.server-badge {
|
||||
font-size: 0.7em;
|
||||
padding: 4px 8px;
|
||||
border-radius: 4px;
|
||||
text-transform: uppercase;
|
||||
letter-spacing: 1px;
|
||||
}
|
||||
|
||||
.server-badge.foundation {
|
||||
background: rgba(255, 0, 0, 0.2);
|
||||
color: #ff0000;
|
||||
border: 1px solid #ff0000;
|
||||
}
|
||||
|
||||
.server-badge.corporation {
|
||||
background: rgba(0, 102, 255, 0.2);
|
||||
color: #0066ff;
|
||||
border: 1px solid #0066ff;
|
||||
}
|
||||
|
||||
.server-badge.labs {
|
||||
background: rgba(255, 165, 0, 0.2);
|
||||
color: #ffa500;
|
||||
border: 1px solid #ffa500;
|
||||
}
|
||||
|
||||
.channel-list {
|
||||
flex: 1;
|
||||
overflow-y: auto;
|
||||
padding: 8px 0;
|
||||
}
|
||||
|
||||
.channel-category {
|
||||
padding: 16px 16px 8px 16px;
|
||||
font-size: 0.75em;
|
||||
text-transform: uppercase;
|
||||
letter-spacing: 2px;
|
||||
color: #666;
|
||||
font-weight: 700;
|
||||
}
|
||||
|
||||
.channel-item {
|
||||
padding: 8px 16px;
|
||||
margin: 2px 8px;
|
||||
border-radius: 4px;
|
||||
cursor: pointer;
|
||||
transition: all 0.2s;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
font-size: 0.95em;
|
||||
}
|
||||
|
||||
.channel-item:hover {
|
||||
background: #1a1a1a;
|
||||
}
|
||||
|
||||
.channel-item.active {
|
||||
background: #1a1a1a;
|
||||
}
|
||||
|
||||
.channel-icon {
|
||||
color: #666;
|
||||
}
|
||||
|
||||
.channel-name {
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
.channel-badge {
|
||||
font-size: 0.75em;
|
||||
background: #ff0000;
|
||||
color: #fff;
|
||||
padding: 2px 6px;
|
||||
border-radius: 10px;
|
||||
}
|
||||
|
||||
/* User Presence Panel */
|
||||
.user-presence {
|
||||
padding: 12px 16px;
|
||||
border-top: 1px solid #1a1a1a;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 12px;
|
||||
font-size: 0.9em;
|
||||
}
|
||||
|
||||
.user-avatar {
|
||||
width: 40px;
|
||||
height: 40px;
|
||||
background: linear-gradient(135deg, #ff0000, #0066ff, #ffa500);
|
||||
border-radius: 50%;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
font-weight: 700;
|
||||
}
|
||||
|
||||
.user-info {
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
.user-name {
|
||||
font-weight: 700;
|
||||
margin-bottom: 2px;
|
||||
}
|
||||
|
||||
.user-status {
|
||||
font-size: 0.85em;
|
||||
color: #666;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 6px;
|
||||
}
|
||||
|
||||
.status-dot {
|
||||
width: 8px;
|
||||
height: 8px;
|
||||
border-radius: 50%;
|
||||
background: #00ff00;
|
||||
box-shadow: 0 0 8px #00ff00;
|
||||
}
|
||||
|
||||
/* Chat Area */
|
||||
.chat-area {
|
||||
flex: 1;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
background: #0a0a0a;
|
||||
}
|
||||
|
||||
.chat-header {
|
||||
padding: 16px 20px;
|
||||
border-bottom: 1px solid #1a1a1a;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 12px;
|
||||
}
|
||||
|
||||
.channel-name-header {
|
||||
flex: 1;
|
||||
font-weight: 700;
|
||||
font-size: 1.1em;
|
||||
}
|
||||
|
||||
.chat-tools {
|
||||
display: flex;
|
||||
gap: 16px;
|
||||
font-size: 0.9em;
|
||||
color: #666;
|
||||
}
|
||||
|
||||
.chat-tool {
|
||||
cursor: pointer;
|
||||
transition: color 0.2s;
|
||||
}
|
||||
|
||||
.chat-tool:hover {
|
||||
color: #0066ff;
|
||||
}
|
||||
|
||||
.chat-messages {
|
||||
flex: 1;
|
||||
overflow-y: auto;
|
||||
padding: 20px;
|
||||
}
|
||||
|
||||
.message {
|
||||
display: flex;
|
||||
gap: 16px;
|
||||
margin-bottom: 20px;
|
||||
padding: 12px;
|
||||
border-radius: 4px;
|
||||
transition: background 0.2s;
|
||||
}
|
||||
|
||||
.message:hover {
|
||||
background: #0f0f0f;
|
||||
}
|
||||
|
||||
.message-avatar {
|
||||
width: 40px;
|
||||
height: 40px;
|
||||
border-radius: 50%;
|
||||
background: #1a1a1a;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
font-weight: 700;
|
||||
font-size: 0.9em;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.message-content {
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
.message-header {
|
||||
display: flex;
|
||||
align-items: baseline;
|
||||
gap: 12px;
|
||||
margin-bottom: 4px;
|
||||
}
|
||||
|
||||
.message-author {
|
||||
font-weight: 700;
|
||||
}
|
||||
|
||||
.message-time {
|
||||
font-size: 0.75em;
|
||||
color: #666;
|
||||
}
|
||||
|
||||
.message-badge {
|
||||
font-size: 0.65em;
|
||||
padding: 2px 6px;
|
||||
border-radius: 3px;
|
||||
text-transform: uppercase;
|
||||
letter-spacing: 1px;
|
||||
}
|
||||
|
||||
.message-badge.foundation {
|
||||
background: rgba(255, 0, 0, 0.2);
|
||||
color: #ff0000;
|
||||
}
|
||||
|
||||
.message-badge.labs {
|
||||
background: rgba(255, 165, 0, 0.2);
|
||||
color: #ffa500;
|
||||
}
|
||||
|
||||
.message-text {
|
||||
line-height: 1.6;
|
||||
color: #ccc;
|
||||
}
|
||||
|
||||
.message-system {
|
||||
background: #0f0f0f;
|
||||
border-left: 3px solid;
|
||||
padding: 12px;
|
||||
margin-bottom: 16px;
|
||||
font-size: 0.9em;
|
||||
}
|
||||
|
||||
.message-system.foundation {
|
||||
border-color: #ff0000;
|
||||
}
|
||||
|
||||
.message-system.corporation {
|
||||
border-color: #0066ff;
|
||||
}
|
||||
|
||||
.message-system.labs {
|
||||
border-color: #ffa500;
|
||||
}
|
||||
|
||||
.system-label {
|
||||
font-size: 0.75em;
|
||||
text-transform: uppercase;
|
||||
letter-spacing: 1px;
|
||||
margin-bottom: 6px;
|
||||
font-weight: 700;
|
||||
}
|
||||
|
||||
.system-label.foundation { color: #ff0000; }
|
||||
.system-label.corporation { color: #0066ff; }
|
||||
.system-label.labs { color: #ffa500; }
|
||||
|
||||
/* Message Input */
|
||||
.message-input-container {
|
||||
padding: 20px;
|
||||
border-top: 1px solid #1a1a1a;
|
||||
}
|
||||
|
||||
.message-input {
|
||||
background: #0f0f0f;
|
||||
border: 1px solid #1a1a1a;
|
||||
border-radius: 8px;
|
||||
padding: 12px 16px;
|
||||
width: 100%;
|
||||
color: #e0e0e0;
|
||||
font-family: 'Roboto Mono', monospace;
|
||||
font-size: 0.95em;
|
||||
transition: border-color 0.3s;
|
||||
}
|
||||
|
||||
.message-input:focus {
|
||||
outline: none;
|
||||
border-color: #0066ff;
|
||||
}
|
||||
|
||||
.message-input::placeholder {
|
||||
color: #666;
|
||||
}
|
||||
|
||||
/* Member Sidebar */
|
||||
.member-sidebar {
|
||||
width: 280px;
|
||||
background: #0f0f0f;
|
||||
border-left: 1px solid #1a1a1a;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.member-header {
|
||||
padding: 16px;
|
||||
border-bottom: 1px solid #1a1a1a;
|
||||
font-size: 0.85em;
|
||||
text-transform: uppercase;
|
||||
letter-spacing: 2px;
|
||||
color: #666;
|
||||
}
|
||||
|
||||
.member-list {
|
||||
flex: 1;
|
||||
overflow-y: auto;
|
||||
padding: 12px 0;
|
||||
}
|
||||
|
||||
.member-section {
|
||||
margin-bottom: 16px;
|
||||
}
|
||||
|
||||
.member-section-title {
|
||||
padding: 8px 16px;
|
||||
font-size: 0.75em;
|
||||
text-transform: uppercase;
|
||||
letter-spacing: 1px;
|
||||
color: #666;
|
||||
font-weight: 700;
|
||||
}
|
||||
|
||||
.member-item {
|
||||
padding: 6px 16px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 12px;
|
||||
cursor: pointer;
|
||||
transition: background 0.2s;
|
||||
}
|
||||
|
||||
.member-item:hover {
|
||||
background: #1a1a1a;
|
||||
}
|
||||
|
||||
.member-avatar-small {
|
||||
width: 32px;
|
||||
height: 32px;
|
||||
border-radius: 50%;
|
||||
background: #1a1a1a;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
font-size: 0.8em;
|
||||
font-weight: 700;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.online-indicator {
|
||||
position: absolute;
|
||||
bottom: -2px;
|
||||
right: -2px;
|
||||
width: 12px;
|
||||
height: 12px;
|
||||
border-radius: 50%;
|
||||
border: 2px solid #0f0f0f;
|
||||
}
|
||||
|
||||
.online-indicator.online { background: #00ff00; }
|
||||
.online-indicator.in-game { background: #0066ff; }
|
||||
.online-indicator.labs { background: #ffa500; }
|
||||
|
||||
.member-name {
|
||||
flex: 1;
|
||||
font-size: 0.9em;
|
||||
}
|
||||
|
||||
.member-activity {
|
||||
font-size: 0.75em;
|
||||
color: #666;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div class="connect-container">
|
||||
<!-- Server List -->
|
||||
<div class="server-list">
|
||||
<div class="server-icon foundation active">F</div>
|
||||
<div class="server-icon corporation">C</div>
|
||||
<div class="server-icon labs">L</div>
|
||||
<div class="server-divider"></div>
|
||||
<div class="server-icon community">AG</div>
|
||||
<div class="server-icon community">RD</div>
|
||||
<div class="server-icon community">+</div>
|
||||
</div>
|
||||
|
||||
<!-- Channel Sidebar -->
|
||||
<div class="channel-sidebar">
|
||||
<div class="server-header">
|
||||
<span>AeThex Foundation</span>
|
||||
<span class="server-badge foundation">Official</span>
|
||||
</div>
|
||||
|
||||
<div class="channel-list">
|
||||
<div class="channel-category">Announcements</div>
|
||||
<div class="channel-item">
|
||||
<span class="channel-icon">📢</span>
|
||||
<span class="channel-name">updates</span>
|
||||
<span class="channel-badge">3</span>
|
||||
</div>
|
||||
<div class="channel-item">
|
||||
<span class="channel-icon">📜</span>
|
||||
<span class="channel-name">changelog</span>
|
||||
</div>
|
||||
|
||||
<div class="channel-category">Development</div>
|
||||
<div class="channel-item active">
|
||||
<span class="channel-icon">#</span>
|
||||
<span class="channel-name">general</span>
|
||||
</div>
|
||||
<div class="channel-item">
|
||||
<span class="channel-icon">#</span>
|
||||
<span class="channel-name">api-discussion</span>
|
||||
</div>
|
||||
<div class="channel-item">
|
||||
<span class="channel-icon">#</span>
|
||||
<span class="channel-name">passport-development</span>
|
||||
</div>
|
||||
|
||||
<div class="channel-category">Support</div>
|
||||
<div class="channel-item">
|
||||
<span class="channel-icon">❓</span>
|
||||
<span class="channel-name">help</span>
|
||||
</div>
|
||||
<div class="channel-item">
|
||||
<span class="channel-icon">🐛</span>
|
||||
<span class="channel-name">bug-reports</span>
|
||||
</div>
|
||||
|
||||
<div class="channel-category">Voice Channels</div>
|
||||
<div class="channel-item">
|
||||
<span class="channel-icon">🔊</span>
|
||||
<span class="channel-name">Nexus Lounge</span>
|
||||
<span style="color: #666; font-size: 0.8em;">3</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="user-presence">
|
||||
<div class="user-avatar">A</div>
|
||||
<div class="user-info">
|
||||
<div class="user-name">Anderson</div>
|
||||
<div class="user-status">
|
||||
<span class="status-dot"></span>
|
||||
<span>Building AeThex</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Chat Area -->
|
||||
<div class="chat-area">
|
||||
<div class="chat-header">
|
||||
<span class="channel-name-header"># general</span>
|
||||
<div class="chat-tools">
|
||||
<span class="chat-tool">🔔</span>
|
||||
<span class="chat-tool">📌</span>
|
||||
<span class="chat-tool">👥 128</span>
|
||||
<span class="chat-tool">🔍</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="chat-messages">
|
||||
<div class="message-system foundation">
|
||||
<div class="system-label foundation">[FOUNDATION] System Announcement</div>
|
||||
<div>Foundation authentication services upgraded to v2.1.0. Enhanced security protocols now active across all AeThex infrastructure.</div>
|
||||
</div>
|
||||
|
||||
<div class="message">
|
||||
<div class="message-avatar" style="background: linear-gradient(135deg, #ff0000, #cc0000);">T</div>
|
||||
<div class="message-content">
|
||||
<div class="message-header">
|
||||
<span class="message-author">Trevor</span>
|
||||
<span class="message-badge foundation">Foundation</span>
|
||||
<span class="message-time">10:34 AM</span>
|
||||
</div>
|
||||
<div class="message-text">
|
||||
Just pushed the authentication updates. All services should automatically migrate to the new protocols within 24 hours.
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="message">
|
||||
<div class="message-avatar" style="background: linear-gradient(135deg, #0066ff, #003380);">M</div>
|
||||
<div class="message-content">
|
||||
<div class="message-header">
|
||||
<span class="message-author">Marcus</span>
|
||||
<span class="message-time">10:41 AM</span>
|
||||
</div>
|
||||
<div class="message-text">
|
||||
Excellent work! I've been testing the new Passport integration and it's incredibly smooth. The Trinity color-coding in the UI makes it really clear which division is handling what.
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="message-system labs">
|
||||
<div class="system-label labs">[LABS] Experimental Feature Alert</div>
|
||||
<div>Nexus Engine v2.0-beta now available for testing. New cross-platform sync reduces latency by 40%. Join #labs-testing to participate.</div>
|
||||
</div>
|
||||
|
||||
<div class="message">
|
||||
<div class="message-avatar" style="background: linear-gradient(135deg, #ffa500, #ff8c00);">S</div>
|
||||
<div class="message-content">
|
||||
<div class="message-header">
|
||||
<span class="message-author">Sarah</span>
|
||||
<span class="message-badge labs">Labs</span>
|
||||
<span class="message-time">11:15 AM</span>
|
||||
</div>
|
||||
<div class="message-text">
|
||||
The Nexus v2 parallel compilation is insane. Cut my build time from 3 minutes to under 2. Still some edge cases with complex state synchronization but wow... ⚠️
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="message">
|
||||
<div class="message-avatar" style="background: linear-gradient(135deg, #ff0000, #0066ff, #ffa500);">A</div>
|
||||
<div class="message-content">
|
||||
<div class="message-header">
|
||||
<span class="message-author">Anderson</span>
|
||||
<span class="message-badge foundation">Founder</span>
|
||||
<span class="message-time">11:47 AM</span>
|
||||
</div>
|
||||
<div class="message-text">
|
||||
Love seeing the Trinity infrastructure working in harmony. Foundation keeping everything secure, Labs pushing the boundaries, Corporation delivering production-ready tools. This is exactly the vision.
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="message">
|
||||
<div class="message-avatar" style="background: #1a1a1a;">D</div>
|
||||
<div class="message-content">
|
||||
<div class="message-header">
|
||||
<span class="message-author">DevUser_2847</span>
|
||||
<span class="message-time">12:03 PM</span>
|
||||
</div>
|
||||
<div class="message-text">
|
||||
Quick question - when using AeThex Studio, does the Terminal automatically connect to all three Trinity divisions, or do I need to configure that?
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="message-system corporation">
|
||||
<div class="system-label corporation">[CORPORATION] Service Update</div>
|
||||
<div>AeThex Studio Pro users: New Railway deployment templates available. Optimized configurations for Foundation APIs, Corporation services, and Labs experiments.</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="message-input-container">
|
||||
<input
|
||||
type="text"
|
||||
class="message-input"
|
||||
placeholder="Message #general (Foundation infrastructure channel)"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Member Sidebar -->
|
||||
<div class="member-sidebar">
|
||||
<div class="member-header">Members — 128</div>
|
||||
|
||||
<div class="member-list">
|
||||
<div class="member-section">
|
||||
<div class="member-section-title">Foundation Team — 8</div>
|
||||
<div class="member-item">
|
||||
<div class="member-avatar-small" style="background: linear-gradient(135deg, #ff0000, #cc0000);">
|
||||
A
|
||||
<div class="online-indicator online"></div>
|
||||
</div>
|
||||
<div class="member-name">Anderson</div>
|
||||
</div>
|
||||
<div class="member-item">
|
||||
<div class="member-avatar-small" style="background: linear-gradient(135deg, #ff0000, #cc0000);">
|
||||
T
|
||||
<div class="online-indicator online"></div>
|
||||
</div>
|
||||
<div class="member-name">Trevor</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="member-section">
|
||||
<div class="member-section-title">Labs Team — 12</div>
|
||||
<div class="member-item">
|
||||
<div class="member-avatar-small" style="background: linear-gradient(135deg, #ffa500, #ff8c00);">
|
||||
S
|
||||
<div class="online-indicator labs"></div>
|
||||
</div>
|
||||
<div class="member-name">Sarah</div>
|
||||
<div class="member-activity">Testing v2.0</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="member-section">
|
||||
<div class="member-section-title">Developers — 47</div>
|
||||
<div class="member-item">
|
||||
<div class="member-avatar-small">
|
||||
M
|
||||
<div class="online-indicator in-game"></div>
|
||||
</div>
|
||||
<div class="member-name">Marcus</div>
|
||||
<div class="member-activity">Building</div>
|
||||
</div>
|
||||
<div class="member-item">
|
||||
<div class="member-avatar-small">
|
||||
D
|
||||
<div class="online-indicator online"></div>
|
||||
</div>
|
||||
<div class="member-name">DevUser_2847</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="member-section">
|
||||
<div class="member-section-title">Community — 61</div>
|
||||
<div class="member-item">
|
||||
<div class="member-avatar-small">J</div>
|
||||
<div class="member-name">JohnDev</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
|
|
@ -1,7 +1,8 @@
|
|||
import { defineConfig } from 'astro/config';
|
||||
import tailwind from '@astrojs/tailwind';
|
||||
import react from '@astrojs/react';
|
||||
|
||||
export default defineConfig({
|
||||
integrations: [tailwind()],
|
||||
integrations: [tailwind(), react()],
|
||||
site: 'https://aethex-connect.com',
|
||||
});
|
||||
|
|
|
|||
1441
astro-site/package-lock.json
generated
1441
astro-site/package-lock.json
generated
File diff suppressed because it is too large
Load diff
|
|
@ -8,7 +8,14 @@
|
|||
"preview": "astro preview"
|
||||
},
|
||||
"dependencies": {
|
||||
"astro": "^4.0.0"
|
||||
"@astrojs/react": "^4.4.2",
|
||||
"@types/react": "^19.2.8",
|
||||
"@types/react-dom": "^19.2.3",
|
||||
"astro": "^4.0.0",
|
||||
"mumble-client": "^1.3.0",
|
||||
"react": "^19.2.3",
|
||||
"react-dom": "^19.2.3",
|
||||
"simple-peer": "^9.11.1"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@astrojs/tailwind": "^5.0.0",
|
||||
|
|
|
|||
87
astro-site/src/components/auth/AccountLinker.jsx
Normal file
87
astro-site/src/components/auth/AccountLinker.jsx
Normal file
|
|
@ -0,0 +1,87 @@
|
|||
import React, { useState } from "react";
|
||||
|
||||
/**
|
||||
* UI for linking AeThex and Matrix accounts.
|
||||
* 1. User logs in with AeThex credentials (simulate for now)
|
||||
* 2. User logs in with Matrix credentials
|
||||
* 3. Store mapping in localStorage (or call backend in real app)
|
||||
*/
|
||||
export default function AccountLinker({ onLinked }) {
|
||||
const [aethexUser, setAethexUser] = useState("");
|
||||
const [aethexPass, setAethexPass] = useState("");
|
||||
const [matrixUser, setMatrixUser] = useState("");
|
||||
const [matrixPass, setMatrixPass] = useState("");
|
||||
const [step, setStep] = useState(1);
|
||||
const [error, setError] = useState(null);
|
||||
|
||||
// Simulate AeThex auth (replace with real API call)
|
||||
const handleAethexLogin = (e) => {
|
||||
e.preventDefault();
|
||||
if (aethexUser && aethexPass) {
|
||||
setStep(2);
|
||||
setError(null);
|
||||
} else {
|
||||
setError("Enter AeThex username and password.");
|
||||
}
|
||||
};
|
||||
|
||||
// Simulate Matrix auth (let MatrixProvider handle real login)
|
||||
const handleMatrixLogin = (e) => {
|
||||
e.preventDefault();
|
||||
if (matrixUser && matrixPass) {
|
||||
// Store mapping (simulate)
|
||||
localStorage.setItem("aethex-matrix-link", JSON.stringify({ aethexUser, matrixUser }));
|
||||
setError(null);
|
||||
if (onLinked) onLinked({ aethexUser, matrixUser, matrixPass });
|
||||
} else {
|
||||
setError("Enter Matrix username and password.");
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="account-linker bg-[#181818] p-6 rounded-lg max-w-md mx-auto mt-10 shadow-lg">
|
||||
<h2 className="text-xl font-bold mb-4 text-white">Link AeThex & Matrix Accounts</h2>
|
||||
{error && <div className="mb-2 text-red-400">{error}</div>}
|
||||
{step === 1 && (
|
||||
<form onSubmit={handleAethexLogin} className="flex flex-col gap-3">
|
||||
<input
|
||||
type="text"
|
||||
placeholder="AeThex Username"
|
||||
className="px-3 py-2 rounded bg-[#222] text-white border border-[#333]"
|
||||
value={aethexUser}
|
||||
onChange={e => setAethexUser(e.target.value)}
|
||||
autoFocus
|
||||
/>
|
||||
<input
|
||||
type="password"
|
||||
placeholder="AeThex Password"
|
||||
className="px-3 py-2 rounded bg-[#222] text-white border border-[#333]"
|
||||
value={aethexPass}
|
||||
onChange={e => setAethexPass(e.target.value)}
|
||||
/>
|
||||
<button type="submit" className="bg-blue-600 text-white rounded py-2 font-semibold mt-2">Continue to Matrix</button>
|
||||
</form>
|
||||
)}
|
||||
{step === 2 && (
|
||||
<form onSubmit={handleMatrixLogin} className="flex flex-col gap-3">
|
||||
<input
|
||||
type="text"
|
||||
placeholder="Matrix Username (@user:matrix.org)"
|
||||
className="px-3 py-2 rounded bg-[#222] text-white border border-[#333]"
|
||||
value={matrixUser}
|
||||
onChange={e => setMatrixUser(e.target.value)}
|
||||
autoFocus
|
||||
/>
|
||||
<input
|
||||
type="password"
|
||||
placeholder="Matrix Password"
|
||||
className="px-3 py-2 rounded bg-[#222] text-white border border-[#333]"
|
||||
value={matrixPass}
|
||||
onChange={e => setMatrixPass(e.target.value)}
|
||||
/>
|
||||
<button type="submit" className="bg-green-600 text-white rounded py-2 font-semibold mt-2">Link Accounts & Login</button>
|
||||
</form>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
68
astro-site/src/components/matrix/MatrixProvider.jsx
Normal file
68
astro-site/src/components/matrix/MatrixProvider.jsx
Normal file
|
|
@ -0,0 +1,68 @@
|
|||
import React, { createContext, useContext, useEffect, useState, useCallback } from "react";
|
||||
import sdk from "matrix-js-sdk";
|
||||
|
||||
const MatrixContext = createContext(null);
|
||||
|
||||
export function useMatrix() {
|
||||
return useContext(MatrixContext);
|
||||
}
|
||||
|
||||
export function MatrixProvider({ children }) {
|
||||
const [client, setClient] = useState(null);
|
||||
const [rooms, setRooms] = useState([]);
|
||||
const [messages, setMessages] = useState([]);
|
||||
const [user, setUser] = useState(null);
|
||||
const [currentRoomId, setCurrentRoomId] = useState(null);
|
||||
const [loading, setLoading] = useState(false);
|
||||
const [error, setError] = useState(null);
|
||||
|
||||
// Login to Matrix
|
||||
const login = useCallback(async (username, password, homeserver = "https://matrix.org") => {
|
||||
setLoading(true);
|
||||
setError(null);
|
||||
try {
|
||||
const matrixClient = sdk.createClient({ baseUrl: homeserver });
|
||||
const resp = await matrixClient.loginWithPassword(username, password);
|
||||
matrixClient.startClient({ initialSyncLimit: 10 });
|
||||
setClient(matrixClient);
|
||||
setUser({ userId: resp.user_id, accessToken: resp.access_token });
|
||||
setLoading(false);
|
||||
// Listen for sync and events
|
||||
matrixClient.on("sync", (state) => {
|
||||
if (state === "PREPARED") {
|
||||
setRooms(matrixClient.getRooms());
|
||||
}
|
||||
});
|
||||
matrixClient.on("Room.timeline", (event, room) => {
|
||||
if (room.roomId === currentRoomId && event.getType() === "m.room.message") {
|
||||
setMessages((msgs) => [...msgs, event.event]);
|
||||
}
|
||||
});
|
||||
} catch (e) {
|
||||
setError(e.message);
|
||||
setLoading(false);
|
||||
}
|
||||
}, [currentRoomId]);
|
||||
|
||||
// Join a room and fetch messages
|
||||
const joinRoom = useCallback(async (roomId) => {
|
||||
if (!client) return;
|
||||
setCurrentRoomId(roomId);
|
||||
const room = client.getRoom(roomId);
|
||||
if (room) {
|
||||
setMessages(room.timeline.filter(e => e.getType() === "m.room.message").map(e => e.event));
|
||||
}
|
||||
}, [client]);
|
||||
|
||||
// Send a message
|
||||
const sendMessage = useCallback(async (roomId, text) => {
|
||||
if (!client) return;
|
||||
await client.sendTextMessage(roomId, text);
|
||||
}, [client]);
|
||||
|
||||
return (
|
||||
<MatrixContext.Provider value={{ client, rooms, messages, user, login, joinRoom, sendMessage, currentRoomId, loading, error }}>
|
||||
{children}
|
||||
</MatrixContext.Provider>
|
||||
);
|
||||
}
|
||||
68
astro-site/src/components/mockup/ChannelSidebar.jsx
Normal file
68
astro-site/src/components/mockup/ChannelSidebar.jsx
Normal file
|
|
@ -0,0 +1,68 @@
|
|||
import React from "react";
|
||||
import { useWebRTC } from "../webrtc/WebRTCProvider.jsx";
|
||||
|
||||
export default function ChannelSidebar() {
|
||||
const { joined, joinVoice, leaveVoice } = useWebRTC();
|
||||
return (
|
||||
<div className="channel-sidebar w-72 bg-[#0f0f0f] border-r border-[#1a1a1a] flex flex-col">
|
||||
{/* Server Header */}
|
||||
<div className="server-header p-4 border-b border-[#1a1a1a] font-bold text-base flex items-center justify-between">
|
||||
<span>AeThex Foundation</span>
|
||||
<span className="server-badge foundation text-xs px-2 py-1 rounded bg-red-900/20 text-red-500 border border-red-500 uppercase tracking-wider">Official</span>
|
||||
</div>
|
||||
{/* Channel List */}
|
||||
<div className="channel-list flex-1 overflow-y-auto py-2">
|
||||
<div className="channel-category px-4 pt-4 pb-2 text-xs uppercase tracking-widest text-gray-500 font-bold">Announcements</div>
|
||||
<div className="channel-item flex items-center gap-2 px-4 py-2 mx-2 rounded cursor-pointer text-sm hover:bg-[#1a1a1a]">
|
||||
<span className="channel-icon">📢</span>
|
||||
<span className="channel-name flex-1">updates</span>
|
||||
<span className="channel-badge text-xs bg-red-600 text-white px-2 rounded-full">3</span>
|
||||
</div>
|
||||
<div className="channel-item flex items-center gap-2 px-4 py-2 mx-2 rounded cursor-pointer text-sm hover:bg-[#1a1a1a]">
|
||||
<span className="channel-icon">📜</span>
|
||||
<span className="channel-name flex-1">changelog</span>
|
||||
</div>
|
||||
<div className="channel-category px-4 pt-4 pb-2 text-xs uppercase tracking-widest text-gray-500 font-bold">Development</div>
|
||||
<div className="channel-item active flex items-center gap-2 px-4 py-2 mx-2 rounded cursor-pointer text-sm bg-[#1a1a1a]">
|
||||
<span className="channel-icon">#</span>
|
||||
<span className="channel-name flex-1">general</span>
|
||||
</div>
|
||||
<div className="channel-item flex items-center gap-2 px-4 py-2 mx-2 rounded cursor-pointer text-sm hover:bg-[#1a1a1a]">
|
||||
<span className="channel-icon">#</span>
|
||||
<span className="channel-name flex-1">api-discussion</span>
|
||||
</div>
|
||||
<div className="channel-item flex items-center gap-2 px-4 py-2 mx-2 rounded cursor-pointer text-sm hover:bg-[#1a1a1a]">
|
||||
<span className="channel-icon">#</span>
|
||||
<span className="channel-name flex-1">passport-development</span>
|
||||
</div>
|
||||
<div className="channel-category px-4 pt-4 pb-2 text-xs uppercase tracking-widest text-gray-500 font-bold">Support</div>
|
||||
<div className="channel-item flex items-center gap-2 px-4 py-2 mx-2 rounded cursor-pointer text-sm hover:bg-[#1a1a1a]">
|
||||
<span className="channel-icon">❓</span>
|
||||
<span className="channel-name flex-1">help</span>
|
||||
</div>
|
||||
<div className="channel-item flex items-center gap-2 px-4 py-2 mx-2 rounded cursor-pointer text-sm hover:bg-[#1a1a1a]">
|
||||
<span className="channel-icon">🐛</span>
|
||||
<span className="channel-name flex-1">bug-reports</span>
|
||||
</div>
|
||||
<div className="channel-category px-4 pt-4 pb-2 text-xs uppercase tracking-widest text-gray-500 font-bold">Voice Channels</div>
|
||||
<div className={`channel-item flex items-center gap-2 px-4 py-2 mx-2 rounded cursor-pointer text-sm hover:bg-[#1a1a1a] ${joined ? "bg-blue-900/30" : ""}`}
|
||||
onClick={joined ? leaveVoice : joinVoice}>
|
||||
<span className="channel-icon">🔊</span>
|
||||
<span className="channel-name flex-1">Nexus Lounge</span>
|
||||
<span className="text-gray-500 text-xs">{joined ? "Connected" : "3"}</span>
|
||||
</div>
|
||||
</div>
|
||||
{/* User Presence */}
|
||||
<div className="user-presence p-3 border-t border-[#1a1a1a] flex items-center gap-3 text-sm">
|
||||
<div className="user-avatar w-10 h-10 rounded-full flex items-center justify-center font-bold bg-gradient-to-tr from-red-600 via-blue-600 to-orange-400">A</div>
|
||||
<div className="user-info flex-1">
|
||||
<div className="user-name font-bold mb-0.5">Anderson</div>
|
||||
<div className="user-status flex items-center gap-1 text-xs text-gray-500">
|
||||
<span className="status-dot w-2 h-2 rounded-full bg-green-400 shadow-green-400/50 shadow" />
|
||||
<span>Building AeThex</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
64
astro-site/src/components/mockup/ChatArea.jsx
Normal file
64
astro-site/src/components/mockup/ChatArea.jsx
Normal file
|
|
@ -0,0 +1,64 @@
|
|||
import React, { useEffect } from "react";
|
||||
import Message from "./Message";
|
||||
import MessageInput from "./MessageInput";
|
||||
import { useMatrix } from "../matrix/MatrixProvider.jsx";
|
||||
|
||||
// Default room to join (replace with your Matrix room ID)
|
||||
const DEFAULT_ROOM_ID = "!foundation:matrix.org";
|
||||
|
||||
export default function ChatArea() {
|
||||
const { messages, joinRoom, currentRoomId, user } = useMatrix();
|
||||
|
||||
// Join the default room on login
|
||||
useEffect(() => {
|
||||
if (user && !currentRoomId) {
|
||||
joinRoom(DEFAULT_ROOM_ID);
|
||||
}
|
||||
}, [user, currentRoomId, joinRoom]);
|
||||
|
||||
return (
|
||||
<div className="chat-area flex flex-col flex-1 bg-[#0a0a0a]">
|
||||
{/* Chat Header */}
|
||||
<div className="chat-header px-5 py-4 border-b border-[#1a1a1a] flex items-center gap-3">
|
||||
<span className="channel-name-header flex-1 font-bold text-base"># general</span>
|
||||
<div className="chat-tools flex gap-4 text-sm text-gray-500">
|
||||
<span className="chat-tool cursor-pointer hover:text-blue-500">🔔</span>
|
||||
<span className="chat-tool cursor-pointer hover:text-blue-500">📌</span>
|
||||
<span className="chat-tool cursor-pointer hover:text-blue-500">👥 128</span>
|
||||
<span className="chat-tool cursor-pointer hover:text-blue-500">🔍</span>
|
||||
</div>
|
||||
</div>
|
||||
{/* Messages */}
|
||||
<div className="chat-messages flex-1 overflow-y-auto px-5 py-5">
|
||||
{messages && messages.length > 0 ? (
|
||||
messages.map((msg, i) => (
|
||||
<Message key={i} {...matrixToMessageProps(msg)} />
|
||||
))
|
||||
) : (
|
||||
<div className="text-gray-500 text-center">No messages yet.</div>
|
||||
)}
|
||||
</div>
|
||||
{/* Message Input */}
|
||||
<div className="message-input-container px-5 py-5 border-t border-[#1a1a1a]">
|
||||
<MessageInput />
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
// Helper to convert Matrix event to Message props
|
||||
function matrixToMessageProps(event) {
|
||||
if (!event) return {};
|
||||
if (event.type === "m.room.message") {
|
||||
return {
|
||||
type: "user",
|
||||
author: event.sender?.split(":")[0]?.replace("@", "") || "User",
|
||||
text: event.content?.body || "",
|
||||
time: new Date(event.origin_server_ts).toLocaleTimeString([], { hour: "2-digit", minute: "2-digit" }),
|
||||
avatar: event.sender?.charAt(1)?.toUpperCase() || "U",
|
||||
avatarBg: "from-blue-600 to-blue-900",
|
||||
};
|
||||
}
|
||||
// Add system message mapping if needed
|
||||
return { type: "system", label: "MATRIX", text: JSON.stringify(event), className: "foundation" };
|
||||
}
|
||||
14
astro-site/src/components/mockup/MainLayout.jsx
Normal file
14
astro-site/src/components/mockup/MainLayout.jsx
Normal file
|
|
@ -0,0 +1,14 @@
|
|||
import { WebRTCProvider } from "../webrtc/WebRTCProvider.jsx";
|
||||
|
||||
export default function MainLayout() {
|
||||
return (
|
||||
<WebRTCProvider>
|
||||
<div className="connect-container flex h-screen">
|
||||
<ServerList />
|
||||
<ChannelSidebar />
|
||||
<ChatArea />
|
||||
<MemberSidebar />
|
||||
</div>
|
||||
</WebRTCProvider>
|
||||
);
|
||||
}
|
||||
98
astro-site/src/components/mockup/MemberSidebar.jsx
Normal file
98
astro-site/src/components/mockup/MemberSidebar.jsx
Normal file
|
|
@ -0,0 +1,98 @@
|
|||
import React, { useEffect, useRef } from "react";
|
||||
import { useWebRTC } from "../webrtc/WebRTCProvider.jsx";
|
||||
|
||||
const members = [
|
||||
{ section: "Foundation Team — 8", users: [
|
||||
{ name: "Anderson", avatar: "A", status: "online", avatarBg: "from-red-600 to-red-800" },
|
||||
{ name: "Trevor", avatar: "T", status: "online", avatarBg: "from-red-600 to-red-800" },
|
||||
]},
|
||||
{ section: "Labs Team — 12", users: [
|
||||
{ name: "Sarah", avatar: "S", status: "labs", avatarBg: "from-orange-400 to-orange-700", activity: "Testing v2.0" },
|
||||
]},
|
||||
{ section: "Developers — 47", users: [
|
||||
{ name: "Marcus", avatar: "M", status: "in-game", avatarBg: "bg-[#1a1a1a]", activity: "Building" },
|
||||
{ name: "DevUser_2847", avatar: "D", status: "online", avatarBg: "bg-[#1a1a1a]" },
|
||||
]},
|
||||
{ section: "Community — 61", users: [
|
||||
{ name: "JohnDev", avatar: "J", status: "offline", avatarBg: "bg-[#1a1a1a]" },
|
||||
]},
|
||||
];
|
||||
|
||||
export default function MemberSidebar() {
|
||||
const { joined, peers, localStream } = useWebRTC();
|
||||
|
||||
// Helper to render audio for remote streams
|
||||
function RemoteAudio({ stream }) {
|
||||
const audioRef = useRef();
|
||||
useEffect(() => {
|
||||
if (audioRef.current && stream) {
|
||||
audioRef.current.srcObject = stream;
|
||||
}
|
||||
}, [stream]);
|
||||
return <audio ref={audioRef} autoPlay playsInline />;
|
||||
}
|
||||
|
||||
// Helper to render local audio (muted)
|
||||
function LocalAudio() {
|
||||
const audioRef = useRef();
|
||||
useEffect(() => {
|
||||
if (audioRef.current && localStream) {
|
||||
audioRef.current.srcObject = localStream;
|
||||
}
|
||||
}, [localStream]);
|
||||
return <audio ref={audioRef} autoPlay playsInline muted />;
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="member-sidebar w-72 bg-[#0f0f0f] border-l border-[#1a1a1a] flex flex-col">
|
||||
<div className="member-header p-4 border-b border-[#1a1a1a] text-xs uppercase tracking-widest text-gray-500">Members — 128</div>
|
||||
<div className="member-list flex-1 overflow-y-auto py-3">
|
||||
{/* Show all connected voice users */}
|
||||
{(joined || (peers && peers.length > 0)) && (
|
||||
<div className="member-section mb-4">
|
||||
<div className="member-section-title px-4 py-2 text-xs uppercase tracking-wider text-blue-400 font-bold">Voice Channel — Nexus Lounge</div>
|
||||
{/* Local user */}
|
||||
{joined && (
|
||||
<div className="member-item flex items-center gap-3 px-4 py-1.5 cursor-pointer bg-blue-900/20">
|
||||
<div className="member-avatar-small w-8 h-8 rounded-full flex items-center justify-center font-bold text-sm relative bg-gradient-to-tr from-blue-600 to-blue-900">
|
||||
<span role="img" aria-label="mic">🎤</span>
|
||||
<div className="online-indicator absolute -bottom-0.5 -right-0.5 w-3 h-3 rounded-full border-2 border-[#0f0f0f] bg-blue-400"></div>
|
||||
</div>
|
||||
<div className="member-name flex-1 text-sm">You (Voice Connected)</div>
|
||||
<div className="member-activity text-xs text-blue-400">Live</div>
|
||||
<LocalAudio />
|
||||
</div>
|
||||
)}
|
||||
{/* Remote peers */}
|
||||
{peers && peers.map(({ peerId, stream }) => (
|
||||
<div key={peerId} className="member-item flex items-center gap-3 px-4 py-1.5 cursor-pointer bg-blue-900/10">
|
||||
<div className="member-avatar-small w-8 h-8 rounded-full flex items-center justify-center font-bold text-sm relative bg-gradient-to-tr from-blue-600 to-blue-900">
|
||||
<span role="img" aria-label="mic">🎤</span>
|
||||
<div className="online-indicator absolute -bottom-0.5 -right-0.5 w-3 h-3 rounded-full border-2 border-[#0f0f0f] bg-blue-400"></div>
|
||||
</div>
|
||||
<div className="member-name flex-1 text-sm">{peerId}</div>
|
||||
<div className="member-activity text-xs text-blue-400">Live</div>
|
||||
<RemoteAudio stream={stream} />
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
)}
|
||||
{members.map((section, i) => (
|
||||
<div key={i} className="member-section mb-4">
|
||||
<div className="member-section-title px-4 py-2 text-xs uppercase tracking-wider text-gray-500 font-bold">{section.section}</div>
|
||||
{section.users.map((user, j) => (
|
||||
<div key={j} className="member-item flex items-center gap-3 px-4 py-1.5 cursor-pointer hover:bg-[#1a1a1a]">
|
||||
<div className={`member-avatar-small w-8 h-8 rounded-full flex items-center justify-center font-bold text-sm relative bg-gradient-to-tr ${user.avatarBg}`}>
|
||||
{user.avatar}
|
||||
<div className={`online-indicator absolute -bottom-0.5 -right-0.5 w-3 h-3 rounded-full border-2 border-[#0f0f0f] ${user.status === "online" ? "bg-green-400" : user.status === "in-game" ? "bg-blue-500" : user.status === "labs" ? "bg-orange-400" : "bg-gray-700"}`}></div>
|
||||
</div>
|
||||
<div className="member-name flex-1 text-sm">{user.name}</div>
|
||||
{user.activity && <div className="member-activity text-xs text-gray-500">{user.activity}</div>}
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
27
astro-site/src/components/mockup/Message.jsx
Normal file
27
astro-site/src/components/mockup/Message.jsx
Normal file
|
|
@ -0,0 +1,27 @@
|
|||
import React from "react";
|
||||
|
||||
export default function Message(props) {
|
||||
if (props.type === "system") {
|
||||
return (
|
||||
<div className={`message-system ${props.className} bg-[#0f0f0f] border-l-4 pl-4 pr-4 py-3 mb-4 text-sm`}>
|
||||
<div className={`system-label ${props.className} text-xs uppercase tracking-wider font-bold mb-1`}>[{props.label}] System Announcement</div>
|
||||
<div>{props.text}</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
return (
|
||||
<div className="message flex gap-4 mb-5 p-3 rounded transition hover:bg-[#0f0f0f]">
|
||||
<div className={`message-avatar w-10 h-10 rounded-full flex items-center justify-center font-bold text-base flex-shrink-0 bg-gradient-to-tr ${props.avatarBg}`}>{props.avatar}</div>
|
||||
<div className="message-content flex-1">
|
||||
<div className="message-header flex items-baseline gap-3 mb-1">
|
||||
<span className="message-author font-bold">{props.author}</span>
|
||||
{props.badge && (
|
||||
<span className={`message-badge ${props.className} text-xs px-2 py-1 rounded uppercase tracking-wider font-bold`}>{props.badge}</span>
|
||||
)}
|
||||
<span className="message-time text-xs text-gray-500">{props.time}</span>
|
||||
</div>
|
||||
<div className="message-text leading-relaxed text-gray-300">{props.text}</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
32
astro-site/src/components/mockup/MessageInput.jsx
Normal file
32
astro-site/src/components/mockup/MessageInput.jsx
Normal file
|
|
@ -0,0 +1,32 @@
|
|||
import React, { useState } from "react";
|
||||
import { useMatrix } from "../matrix/MatrixProvider.jsx";
|
||||
|
||||
const DEFAULT_ROOM_ID = "!foundation:matrix.org";
|
||||
|
||||
export default function MessageInput() {
|
||||
const [text, setText] = useState("");
|
||||
const { sendMessage, user, currentRoomId } = useMatrix();
|
||||
|
||||
const handleSend = async (e) => {
|
||||
e.preventDefault();
|
||||
if (!text.trim() || !user) return;
|
||||
await sendMessage(currentRoomId || DEFAULT_ROOM_ID, text);
|
||||
setText("");
|
||||
};
|
||||
|
||||
return (
|
||||
<form className="flex items-center gap-2" onSubmit={handleSend}>
|
||||
<button type="button" className="attachButton w-10 h-10 flex items-center justify-center rounded bg-[#1a1a1a] text-xl text-gray-400 mr-2">+</button>
|
||||
<input
|
||||
type="text"
|
||||
className="message-input flex-1 bg-[#0f0f0f] border border-[#1a1a1a] rounded-lg px-4 py-3 text-gray-200 text-sm focus:outline-none focus:border-blue-500 placeholder:text-gray-500"
|
||||
placeholder="Message #general (Foundation infrastructure channel)"
|
||||
maxLength={2000}
|
||||
value={text}
|
||||
onChange={e => setText(e.target.value)}
|
||||
disabled={!user}
|
||||
/>
|
||||
<button type="submit" className="sendButton w-10 h-10 flex items-center justify-center rounded bg-blue-600 text-xl text-white ml-2">3a4</button>
|
||||
</form>
|
||||
);
|
||||
}
|
||||
30
astro-site/src/components/mockup/ServerList.jsx
Normal file
30
astro-site/src/components/mockup/ServerList.jsx
Normal file
|
|
@ -0,0 +1,30 @@
|
|||
import React from "react";
|
||||
|
||||
const servers = [
|
||||
{ id: "foundation", label: "F", active: true, className: "foundation" },
|
||||
{ id: "corporation", label: "C", active: false, className: "corporation" },
|
||||
{ id: "labs", label: "L", active: false, className: "labs" },
|
||||
{ id: "divider" },
|
||||
{ id: "community1", label: "AG", active: false, className: "community" },
|
||||
{ id: "community2", label: "RD", active: false, className: "community" },
|
||||
{ id: "add", label: "+", active: false, className: "community" },
|
||||
];
|
||||
|
||||
export default function ServerList() {
|
||||
return (
|
||||
<div className="server-list flex flex-col items-center py-3 gap-3 w-20 bg-[#0d0d0d] border-r border-[#1a1a1a]">
|
||||
{servers.map((srv, i) =>
|
||||
srv.id === "divider" ? (
|
||||
<div key={i} className="server-divider w-10 h-0.5 bg-[#1a1a1a] my-1" />
|
||||
) : (
|
||||
<div
|
||||
key={srv.id}
|
||||
className={`server-icon ${srv.className} ${srv.active ? "active" : ""} w-14 h-14 rounded-xl flex items-center justify-center font-bold text-lg cursor-pointer transition-all relative`}
|
||||
>
|
||||
{srv.label}
|
||||
</div>
|
||||
)
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
57
astro-site/src/components/mockup/global.css
Normal file
57
astro-site/src/components/mockup/global.css
Normal file
|
|
@ -0,0 +1,57 @@
|
|||
@import url('https://fonts.googleapis.com/css2?family=Roboto+Mono:wght@300;400;500;700&display=swap');
|
||||
|
||||
html, body, #root {
|
||||
height: 100%;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
font-family: 'Roboto Mono', monospace;
|
||||
background: #0a0a0a;
|
||||
color: #e0e0e0;
|
||||
}
|
||||
|
||||
body::before {
|
||||
content: '';
|
||||
position: fixed;
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 100vw;
|
||||
height: 100vh;
|
||||
background: repeating-linear-gradient(
|
||||
0deg,
|
||||
rgba(0, 0, 0, 0.15),
|
||||
rgba(0, 0, 0, 0.15) 1px,
|
||||
transparent 1px,
|
||||
transparent 2px
|
||||
);
|
||||
pointer-events: none;
|
||||
z-index: 1000;
|
||||
}
|
||||
|
||||
.connect-container {
|
||||
height: 100vh;
|
||||
display: flex;
|
||||
}
|
||||
|
||||
.server-icon, .user-avatar, .member-avatar-small {
|
||||
background: rgba(26,26,26,0.85);
|
||||
backdrop-filter: blur(6px);
|
||||
}
|
||||
|
||||
.channel-item.active, .channel-item:hover, .member-item:hover {
|
||||
background: rgba(26,26,26,0.85);
|
||||
backdrop-filter: blur(4px);
|
||||
}
|
||||
|
||||
.message-input, .message-input-container {
|
||||
background: rgba(15,15,15,0.95);
|
||||
backdrop-filter: blur(4px);
|
||||
}
|
||||
|
||||
::-webkit-scrollbar {
|
||||
width: 8px;
|
||||
background: #111;
|
||||
}
|
||||
::-webkit-scrollbar-thumb {
|
||||
background: #222;
|
||||
border-radius: 4px;
|
||||
}
|
||||
103
astro-site/src/components/webrtc/WebRTCProvider.jsx
Normal file
103
astro-site/src/components/webrtc/WebRTCProvider.jsx
Normal file
|
|
@ -0,0 +1,103 @@
|
|||
import React, { createContext, useContext, useRef, useState, useEffect, useCallback } from "react";
|
||||
import Peer from "simple-peer";
|
||||
import { useMatrix } from "../matrix/MatrixProvider.jsx";
|
||||
|
||||
const WebRTCContext = createContext(null);
|
||||
|
||||
export function useWebRTC() {
|
||||
return useContext(WebRTCContext);
|
||||
}
|
||||
|
||||
const [peers, setPeers] = useState([]); // [{ peerId, peer, stream }]
|
||||
const [localStream, setLocalStream] = useState(null);
|
||||
const [joined, setJoined] = useState(false);
|
||||
const peersRef = useRef({});
|
||||
const { client, user, currentRoomId } = useMatrix();
|
||||
const SIGNAL_EVENT = "org.aethex.voice.signal";
|
||||
const VOICE_ROOM = currentRoomId || "!foundation:matrix.org";
|
||||
|
||||
// Helper: Send signal via Matrix event
|
||||
const sendSignal = useCallback((to, data) => {
|
||||
if (!client || !VOICE_ROOM) return;
|
||||
client.sendEvent(VOICE_ROOM, SIGNAL_EVENT, { to, from: user?.userId, data }, "");
|
||||
}, [client, VOICE_ROOM, user]);
|
||||
|
||||
// Join a voice channel (start local audio, announce self)
|
||||
const joinVoice = async () => {
|
||||
if (localStream || !client || !user) return;
|
||||
try {
|
||||
const stream = await navigator.mediaDevices.getUserMedia({ audio: true });
|
||||
setLocalStream(stream);
|
||||
setJoined(true);
|
||||
// Announce self to others
|
||||
client.sendEvent(VOICE_ROOM, SIGNAL_EVENT, { join: true, from: user.userId }, "");
|
||||
} catch (err) {
|
||||
alert("Could not access microphone: " + err.message);
|
||||
}
|
||||
};
|
||||
|
||||
// Leave voice channel (close peers, announce leave)
|
||||
const leaveVoice = () => {
|
||||
if (localStream) {
|
||||
localStream.getTracks().forEach(track => track.stop());
|
||||
setLocalStream(null);
|
||||
}
|
||||
Object.values(peersRef.current).forEach(({ peer }) => peer.destroy());
|
||||
peersRef.current = {};
|
||||
setPeers([]);
|
||||
setJoined(false);
|
||||
if (client && user) {
|
||||
client.sendEvent(VOICE_ROOM, SIGNAL_EVENT, { leave: true, from: user.userId }, "");
|
||||
}
|
||||
};
|
||||
|
||||
// Handle incoming Matrix signal events
|
||||
useEffect(() => {
|
||||
if (!client || !user || !VOICE_ROOM) return;
|
||||
const handler = (event) => {
|
||||
if (event.getType() !== SIGNAL_EVENT) return;
|
||||
const { from, to, data, join, leave } = event.getContent();
|
||||
if (from === user.userId) return;
|
||||
// Handle join: create peer if not exists
|
||||
if (join) {
|
||||
if (!peersRef.current[from]) {
|
||||
const initiator = user.userId > from; // simple deterministic initiator
|
||||
const peer = new Peer({ initiator, trickle: false, stream: localStream });
|
||||
peer.on("signal", signal => sendSignal(from, signal));
|
||||
peer.on("stream", remoteStream => {
|
||||
setPeers(p => [...p, { peerId: from, peer, stream: remoteStream }]);
|
||||
});
|
||||
peer.on("close", () => {
|
||||
setPeers(p => p.filter(x => x.peerId !== from));
|
||||
delete peersRef.current[from];
|
||||
});
|
||||
peersRef.current[from] = { peer };
|
||||
}
|
||||
}
|
||||
// Handle leave: remove peer
|
||||
if (leave && peersRef.current[from]) {
|
||||
peersRef.current[from].peer.destroy();
|
||||
delete peersRef.current[from];
|
||||
setPeers(p => p.filter(x => x.peerId !== from));
|
||||
}
|
||||
// Handle signal: pass to peer
|
||||
if (data && peersRef.current[from]) {
|
||||
peersRef.current[from].peer.signal(data);
|
||||
}
|
||||
};
|
||||
client.on("event", handler);
|
||||
return () => client.removeListener("event", handler);
|
||||
}, [client, user, VOICE_ROOM, localStream, sendSignal]);
|
||||
|
||||
// Announce self to new joiners
|
||||
useEffect(() => {
|
||||
if (!joined || !client || !user) return;
|
||||
client.sendEvent(VOICE_ROOM, SIGNAL_EVENT, { join: true, from: user.userId }, "");
|
||||
}, [joined, client, user, VOICE_ROOM]);
|
||||
|
||||
return (
|
||||
<WebRTCContext.Provider value={{ peers, localStream, joined, joinVoice, leaveVoice }}>
|
||||
{children}
|
||||
</WebRTCContext.Provider>
|
||||
);
|
||||
}
|
||||
|
|
@ -1,63 +1,19 @@
|
|||
---
|
||||
import Layout from '../layouts/Layout.astro';
|
||||
import Hero from '../components/Hero.astro';
|
||||
import Features from '../components/Features.astro';
|
||||
import Pricing from '../components/Pricing.astro';
|
||||
import MainLayout from '../components/mockup/MainLayout.jsx';
|
||||
import AccountLinker from '../components/auth/AccountLinker.jsx';
|
||||
import { useState } from 'react';
|
||||
import { MatrixProvider } from '../components/matrix/MatrixProvider.jsx';
|
||||
---
|
||||
|
||||
<Layout title="AeThex Connect - Gaming Communication Platform">
|
||||
<main>
|
||||
<Hero />
|
||||
<Features />
|
||||
<Pricing />
|
||||
|
||||
<!-- Download Section -->
|
||||
<section id="download" class="download">
|
||||
<div class="container">
|
||||
<h2>Download <span class="gradient-text">AeThex Connect</span></h2>
|
||||
<p>Available on all platforms</p>
|
||||
|
||||
<div class="download-buttons">
|
||||
<a href="#" class="download-btn">
|
||||
<span class="icon">🍎</span>
|
||||
<div>
|
||||
<div class="label">Download on the</div>
|
||||
<div class="platform">App Store</div>
|
||||
</div>
|
||||
</a>
|
||||
<a href="#" class="download-btn">
|
||||
<span class="icon">🤖</span>
|
||||
<div>
|
||||
<div class="label">Get it on</div>
|
||||
<div class="platform">Google Play</div>
|
||||
</div>
|
||||
</a>
|
||||
<a href="#" class="download-btn">
|
||||
<span class="icon">💻</span>
|
||||
<div>
|
||||
<div class="label">Desktop for</div>
|
||||
<div class="platform">Windows/Mac/Linux</div>
|
||||
</div>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<!-- Footer -->
|
||||
<footer class="footer">
|
||||
<div class="container">
|
||||
<div class="footer-content">
|
||||
<div class="footer-brand">
|
||||
<h3>AeThex Connect</h3>
|
||||
<p>Next-generation communication for gamers</p>
|
||||
</div>
|
||||
<div class="footer-links">
|
||||
<div>
|
||||
<h4>Product</h4>
|
||||
<ul>
|
||||
<li><a href="#features">Features</a></li>
|
||||
<li><a href="#pricing">Pricing</a></li>
|
||||
<li><a href="#download">Download</a></li>
|
||||
<Layout title="AeThex Connect">
|
||||
<AccountLinker client:load onLinked={({ matrixUser, matrixPass }) => {
|
||||
// Optionally, auto-login to Matrix here
|
||||
// This is a placeholder; actual login logic should be in a React component
|
||||
}} />
|
||||
<MatrixProvider>
|
||||
<MainLayout client:load />
|
||||
</MatrixProvider>
|
||||
</ul>
|
||||
</div>
|
||||
<div>
|
||||
|
|
|
|||
7
astro-site/src/pages/mockup.jsx
Normal file
7
astro-site/src/pages/mockup.jsx
Normal file
|
|
@ -0,0 +1,7 @@
|
|||
import React from "react";
|
||||
import MainLayout from "../components/mockup/MainLayout";
|
||||
import "../components/mockup/global.css";
|
||||
|
||||
export default function MockupPage() {
|
||||
return <MainLayout />;
|
||||
}
|
||||
7
astro-site/tsconfig.json
Normal file
7
astro-site/tsconfig.json
Normal file
|
|
@ -0,0 +1,7 @@
|
|||
{
|
||||
"extends": "astro/tsconfigs/base",
|
||||
"compilerOptions": {
|
||||
"jsx": "react-jsx",
|
||||
"jsxImportSource": "react"
|
||||
}
|
||||
}
|
||||
7
packages/desktop/src/renderer/App.tsx
Normal file
7
packages/desktop/src/renderer/App.tsx
Normal file
|
|
@ -0,0 +1,7 @@
|
|||
import React from "react";
|
||||
import MainLayout from "./components/MainLayout";
|
||||
import "./global.css";
|
||||
|
||||
const App: React.FC = () => <MainLayout />;
|
||||
|
||||
export default App;
|
||||
65
packages/desktop/src/renderer/components/ChannelSidebar.tsx
Normal file
65
packages/desktop/src/renderer/components/ChannelSidebar.tsx
Normal file
|
|
@ -0,0 +1,65 @@
|
|||
import React from "react";
|
||||
|
||||
const ChannelSidebar: React.FC = () => (
|
||||
<div className="channel-sidebar w-72 bg-[#0f0f0f] border-r border-[#1a1a1a] flex flex-col">
|
||||
{/* Server Header */}
|
||||
<div className="server-header p-4 border-b border-[#1a1a1a] font-bold text-base flex items-center justify-between">
|
||||
<span>AeThex Foundation</span>
|
||||
<span className="server-badge foundation text-xs px-2 py-1 rounded bg-red-900/20 text-red-500 border border-red-500 uppercase tracking-wider">Official</span>
|
||||
</div>
|
||||
{/* Channel List */}
|
||||
<div className="channel-list flex-1 overflow-y-auto py-2">
|
||||
<div className="channel-category px-4 pt-4 pb-2 text-xs uppercase tracking-widest text-gray-500 font-bold">Announcements</div>
|
||||
<div className="channel-item flex items-center gap-2 px-4 py-2 mx-2 rounded cursor-pointer text-sm hover:bg-[#1a1a1a]">
|
||||
<span className="channel-icon">📢</span>
|
||||
<span className="channel-name flex-1">updates</span>
|
||||
<span className="channel-badge text-xs bg-red-600 text-white px-2 rounded-full">3</span>
|
||||
</div>
|
||||
<div className="channel-item flex items-center gap-2 px-4 py-2 mx-2 rounded cursor-pointer text-sm hover:bg-[#1a1a1a]">
|
||||
<span className="channel-icon">📜</span>
|
||||
<span className="channel-name flex-1">changelog</span>
|
||||
</div>
|
||||
<div className="channel-category px-4 pt-4 pb-2 text-xs uppercase tracking-widest text-gray-500 font-bold">Development</div>
|
||||
<div className="channel-item active flex items-center gap-2 px-4 py-2 mx-2 rounded cursor-pointer text-sm bg-[#1a1a1a]">
|
||||
<span className="channel-icon">#</span>
|
||||
<span className="channel-name flex-1">general</span>
|
||||
</div>
|
||||
<div className="channel-item flex items-center gap-2 px-4 py-2 mx-2 rounded cursor-pointer text-sm hover:bg-[#1a1a1a]">
|
||||
<span className="channel-icon">#</span>
|
||||
<span className="channel-name flex-1">api-discussion</span>
|
||||
</div>
|
||||
<div className="channel-item flex items-center gap-2 px-4 py-2 mx-2 rounded cursor-pointer text-sm hover:bg-[#1a1a1a]">
|
||||
<span className="channel-icon">#</span>
|
||||
<span className="channel-name flex-1">passport-development</span>
|
||||
</div>
|
||||
<div className="channel-category px-4 pt-4 pb-2 text-xs uppercase tracking-widest text-gray-500 font-bold">Support</div>
|
||||
<div className="channel-item flex items-center gap-2 px-4 py-2 mx-2 rounded cursor-pointer text-sm hover:bg-[#1a1a1a]">
|
||||
<span className="channel-icon">❓</span>
|
||||
<span className="channel-name flex-1">help</span>
|
||||
</div>
|
||||
<div className="channel-item flex items-center gap-2 px-4 py-2 mx-2 rounded cursor-pointer text-sm hover:bg-[#1a1a1a]">
|
||||
<span className="channel-icon">🐛</span>
|
||||
<span className="channel-name flex-1">bug-reports</span>
|
||||
</div>
|
||||
<div className="channel-category px-4 pt-4 pb-2 text-xs uppercase tracking-widest text-gray-500 font-bold">Voice Channels</div>
|
||||
<div className="channel-item flex items-center gap-2 px-4 py-2 mx-2 rounded cursor-pointer text-sm hover:bg-[#1a1a1a]">
|
||||
<span className="channel-icon">🔊</span>
|
||||
<span className="channel-name flex-1">Nexus Lounge</span>
|
||||
<span className="text-gray-500 text-xs">3</span>
|
||||
</div>
|
||||
</div>
|
||||
{/* User Presence */}
|
||||
<div className="user-presence p-3 border-t border-[#1a1a1a] flex items-center gap-3 text-sm">
|
||||
<div className="user-avatar w-10 h-10 rounded-full flex items-center justify-center font-bold bg-gradient-to-tr from-red-600 via-blue-600 to-orange-400">A</div>
|
||||
<div className="user-info flex-1">
|
||||
<div className="user-name font-bold mb-0.5">Anderson</div>
|
||||
<div className="user-status flex items-center gap-1 text-xs text-gray-500">
|
||||
<span className="status-dot w-2 h-2 rounded-full bg-green-400 shadow-green-400/50 shadow" />
|
||||
<span>Building AeThex</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
|
||||
export default ChannelSidebar;
|
||||
41
packages/desktop/src/renderer/components/ChatArea.tsx
Normal file
41
packages/desktop/src/renderer/components/ChatArea.tsx
Normal file
|
|
@ -0,0 +1,41 @@
|
|||
import React from "react";
|
||||
import Message from "./Message";
|
||||
import MessageInput from "./MessageInput";
|
||||
|
||||
const messages = [
|
||||
{ type: "system", label: "FOUNDATION", text: "Foundation authentication services upgraded to v2.1.0. Enhanced security protocols now active across all AeThex infrastructure.", className: "foundation" },
|
||||
{ type: "user", author: "Trevor", badge: "Foundation", time: "10:34 AM", text: "Just pushed the authentication updates. All services should automatically migrate to the new protocols within 24 hours.", avatar: "T", avatarBg: "from-red-600 to-red-800" },
|
||||
{ type: "user", author: "Marcus", time: "10:41 AM", text: "Excellent work! I've been testing the new Passport integration and it's incredibly smooth. The Trinity color-coding in the UI makes it really clear which division is handling what.", avatar: "M", avatarBg: "from-blue-600 to-blue-900" },
|
||||
{ type: "system", label: "LABS", text: "Nexus Engine v2.0-beta now available for testing. New cross-platform sync reduces latency by 40%. Join #labs-testing to participate.", className: "labs" },
|
||||
{ type: "user", author: "Sarah", badge: "Labs", time: "11:15 AM", text: "The Nexus v2 parallel compilation is insane. Cut my build time from 3 minutes to under 2. Still some edge cases with complex state synchronization but wow... ⚠️", avatar: "S", avatarBg: "from-orange-400 to-orange-700" },
|
||||
{ type: "user", author: "Anderson", badge: "Founder", time: "11:47 AM", text: "Love seeing the Trinity infrastructure working in harmony. Foundation keeping everything secure, Labs pushing the boundaries, Corporation delivering production-ready tools. This is exactly the vision.", avatar: "A", avatarBg: "from-red-600 via-blue-600 to-orange-400" },
|
||||
{ type: "user", author: "DevUser_2847", time: "12:03 PM", text: "Quick question - when using AeThex Studio, does the Terminal automatically connect to all three Trinity divisions, or do I need to configure that?", avatar: "D", avatarBg: "bg-[#1a1a1a]" },
|
||||
{ type: "system", label: "CORPORATION", text: "AeThex Studio Pro users: New Railway deployment templates available. Optimized configurations for Foundation APIs, Corporation services, and Labs experiments.", className: "corporation" },
|
||||
];
|
||||
|
||||
const ChatArea: React.FC = () => (
|
||||
<div className="chat-area flex flex-col flex-1 bg-[#0a0a0a]">
|
||||
{/* Chat Header */}
|
||||
<div className="chat-header px-5 py-4 border-b border-[#1a1a1a] flex items-center gap-3">
|
||||
<span className="channel-name-header flex-1 font-bold text-base"># general</span>
|
||||
<div className="chat-tools flex gap-4 text-sm text-gray-500">
|
||||
<span className="chat-tool cursor-pointer hover:text-blue-500">🔔</span>
|
||||
<span className="chat-tool cursor-pointer hover:text-blue-500">📌</span>
|
||||
<span className="chat-tool cursor-pointer hover:text-blue-500">👥 128</span>
|
||||
<span className="chat-tool cursor-pointer hover:text-blue-500">🔍</span>
|
||||
</div>
|
||||
</div>
|
||||
{/* Messages */}
|
||||
<div className="chat-messages flex-1 overflow-y-auto px-5 py-5">
|
||||
{messages.map((msg, i) => (
|
||||
<Message key={i} {...msg} />
|
||||
))}
|
||||
</div>
|
||||
{/* Message Input */}
|
||||
<div className="message-input-container px-5 py-5 border-t border-[#1a1a1a]">
|
||||
<MessageInput />
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
|
||||
export default ChatArea;
|
||||
18
packages/desktop/src/renderer/components/MainLayout.tsx
Normal file
18
packages/desktop/src/renderer/components/MainLayout.tsx
Normal file
|
|
@ -0,0 +1,18 @@
|
|||
import React from "react";
|
||||
import ServerList from "./ServerList";
|
||||
import ChannelSidebar from "./ChannelSidebar";
|
||||
import ChatArea from "./ChatArea";
|
||||
import MemberSidebar from "./MemberSidebar";
|
||||
|
||||
const MainLayout: React.FC = () => {
|
||||
return (
|
||||
<div className="connect-container flex h-screen">
|
||||
<ServerList />
|
||||
<ChannelSidebar />
|
||||
<ChatArea />
|
||||
<MemberSidebar />
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default MainLayout;
|
||||
43
packages/desktop/src/renderer/components/MemberSidebar.tsx
Normal file
43
packages/desktop/src/renderer/components/MemberSidebar.tsx
Normal file
|
|
@ -0,0 +1,43 @@
|
|||
import React from "react";
|
||||
|
||||
const members = [
|
||||
{ section: "Foundation Team — 8", users: [
|
||||
{ name: "Anderson", avatar: "A", status: "online", avatarBg: "from-red-600 to-red-800" },
|
||||
{ name: "Trevor", avatar: "T", status: "online", avatarBg: "from-red-600 to-red-800" },
|
||||
]},
|
||||
{ section: "Labs Team — 12", users: [
|
||||
{ name: "Sarah", avatar: "S", status: "labs", avatarBg: "from-orange-400 to-orange-700", activity: "Testing v2.0" },
|
||||
]},
|
||||
{ section: "Developers — 47", users: [
|
||||
{ name: "Marcus", avatar: "M", status: "in-game", avatarBg: "bg-[#1a1a1a]", activity: "Building" },
|
||||
{ name: "DevUser_2847", avatar: "D", status: "online", avatarBg: "bg-[#1a1a1a]" },
|
||||
]},
|
||||
{ section: "Community — 61", users: [
|
||||
{ name: "JohnDev", avatar: "J", status: "offline", avatarBg: "bg-[#1a1a1a]" },
|
||||
]},
|
||||
];
|
||||
|
||||
const MemberSidebar: React.FC = () => (
|
||||
<div className="member-sidebar w-72 bg-[#0f0f0f] border-l border-[#1a1a1a] flex flex-col">
|
||||
<div className="member-header p-4 border-b border-[#1a1a1a] text-xs uppercase tracking-widest text-gray-500">Members — 128</div>
|
||||
<div className="member-list flex-1 overflow-y-auto py-3">
|
||||
{members.map((section, i) => (
|
||||
<div key={i} className="member-section mb-4">
|
||||
<div className="member-section-title px-4 py-2 text-xs uppercase tracking-wider text-gray-500 font-bold">{section.section}</div>
|
||||
{section.users.map((user, j) => (
|
||||
<div key={j} className="member-item flex items-center gap-3 px-4 py-1.5 cursor-pointer hover:bg-[#1a1a1a]">
|
||||
<div className={`member-avatar-small w-8 h-8 rounded-full flex items-center justify-center font-bold text-sm relative bg-gradient-to-tr ${user.avatarBg}`}>
|
||||
{user.avatar}
|
||||
<div className={`online-indicator absolute -bottom-0.5 -right-0.5 w-3 h-3 rounded-full border-2 border-[#0f0f0f] ${user.status === "online" ? "bg-green-400" : user.status === "in-game" ? "bg-blue-500" : user.status === "labs" ? "bg-orange-400" : "bg-gray-700"}`}></div>
|
||||
</div>
|
||||
<div className="member-name flex-1 text-sm">{user.name}</div>
|
||||
{user.activity && <div className="member-activity text-xs text-gray-500">{user.activity}</div>}
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
|
||||
export default MemberSidebar;
|
||||
41
packages/desktop/src/renderer/components/Message.tsx
Normal file
41
packages/desktop/src/renderer/components/Message.tsx
Normal file
|
|
@ -0,0 +1,41 @@
|
|||
import React from "react";
|
||||
|
||||
interface MessageProps {
|
||||
type: "system" | "user";
|
||||
label?: string;
|
||||
className?: string;
|
||||
author?: string;
|
||||
badge?: string;
|
||||
time?: string;
|
||||
text: string;
|
||||
avatar?: string;
|
||||
avatarBg?: string;
|
||||
}
|
||||
|
||||
const Message: React.FC<MessageProps> = (props) => {
|
||||
if (props.type === "system") {
|
||||
return (
|
||||
<div className={`message-system ${props.className} bg-[#0f0f0f] border-l-4 pl-4 pr-4 py-3 mb-4 text-sm`}>
|
||||
<div className={`system-label ${props.className} text-xs uppercase tracking-wider font-bold mb-1`}>[{props.label}] System Announcement</div>
|
||||
<div>{props.text}</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
return (
|
||||
<div className="message flex gap-4 mb-5 p-3 rounded transition hover:bg-[#0f0f0f]">
|
||||
<div className={`message-avatar w-10 h-10 rounded-full flex items-center justify-center font-bold text-base flex-shrink-0 bg-gradient-to-tr ${props.avatarBg}`}>{props.avatar}</div>
|
||||
<div className="message-content flex-1">
|
||||
<div className="message-header flex items-baseline gap-3 mb-1">
|
||||
<span className="message-author font-bold">{props.author}</span>
|
||||
{props.badge && (
|
||||
<span className={`message-badge ${props.className} text-xs px-2 py-1 rounded uppercase tracking-wider font-bold`}>{props.badge}</span>
|
||||
)}
|
||||
<span className="message-time text-xs text-gray-500">{props.time}</span>
|
||||
</div>
|
||||
<div className="message-text leading-relaxed text-gray-300">{props.text}</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default Message;
|
||||
16
packages/desktop/src/renderer/components/MessageInput.tsx
Normal file
16
packages/desktop/src/renderer/components/MessageInput.tsx
Normal file
|
|
@ -0,0 +1,16 @@
|
|||
import React from "react";
|
||||
|
||||
const MessageInput: React.FC = () => (
|
||||
<div className="flex items-center gap-2">
|
||||
<button className="attachButton w-10 h-10 flex items-center justify-center rounded bg-[#1a1a1a] text-xl text-gray-400 mr-2">+</button>
|
||||
<input
|
||||
type="text"
|
||||
className="message-input flex-1 bg-[#0f0f0f] border border-[#1a1a1a] rounded-lg px-4 py-3 text-gray-200 text-sm focus:outline-none focus:border-blue-500 placeholder:text-gray-500"
|
||||
placeholder="Message #general (Foundation infrastructure channel)"
|
||||
maxLength={2000}
|
||||
/>
|
||||
<button className="sendButton w-10 h-10 flex items-center justify-center rounded bg-blue-600 text-xl text-white ml-2">🎤</button>
|
||||
</div>
|
||||
);
|
||||
|
||||
export default MessageInput;
|
||||
30
packages/desktop/src/renderer/components/ServerList.tsx
Normal file
30
packages/desktop/src/renderer/components/ServerList.tsx
Normal file
|
|
@ -0,0 +1,30 @@
|
|||
import React from "react";
|
||||
|
||||
const servers = [
|
||||
{ id: "foundation", label: "F", active: true, className: "foundation" },
|
||||
{ id: "corporation", label: "C", active: false, className: "corporation" },
|
||||
{ id: "labs", label: "L", active: false, className: "labs" },
|
||||
{ id: "divider" },
|
||||
{ id: "community1", label: "AG", active: false, className: "community" },
|
||||
{ id: "community2", label: "RD", active: false, className: "community" },
|
||||
{ id: "add", label: "+", active: false, className: "community" },
|
||||
];
|
||||
|
||||
const ServerList: React.FC = () => (
|
||||
<div className="server-list flex flex-col items-center py-3 gap-3 w-20 bg-[#0d0d0d] border-r border-[#1a1a1a]">
|
||||
{servers.map((srv, i) =>
|
||||
srv.id === "divider" ? (
|
||||
<div key={i} className="server-divider w-10 h-0.5 bg-[#1a1a1a] my-1" />
|
||||
) : (
|
||||
<div
|
||||
key={srv.id}
|
||||
className={`server-icon ${srv.className} ${srv.active ? "active" : ""} w-14 h-14 rounded-xl flex items-center justify-center font-bold text-lg cursor-pointer transition-all relative`}
|
||||
>
|
||||
{srv.label}
|
||||
</div>
|
||||
)
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
|
||||
export default ServerList;
|
||||
61
packages/desktop/src/renderer/global.css
Normal file
61
packages/desktop/src/renderer/global.css
Normal file
|
|
@ -0,0 +1,61 @@
|
|||
@import url('https://fonts.googleapis.com/css2?family=Roboto+Mono:wght@300;400;500;700&display=swap');
|
||||
|
||||
html, body, #root {
|
||||
height: 100%;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
font-family: 'Roboto Mono', monospace;
|
||||
background: #0a0a0a;
|
||||
color: #e0e0e0;
|
||||
}
|
||||
|
||||
body::before {
|
||||
content: '';
|
||||
position: fixed;
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 100vw;
|
||||
height: 100vh;
|
||||
background: repeating-linear-gradient(
|
||||
0deg,
|
||||
rgba(0, 0, 0, 0.15),
|
||||
rgba(0, 0, 0, 0.15) 1px,
|
||||
transparent 1px,
|
||||
transparent 2px
|
||||
);
|
||||
pointer-events: none;
|
||||
z-index: 1000;
|
||||
}
|
||||
|
||||
.connect-container {
|
||||
height: 100vh;
|
||||
display: flex;
|
||||
}
|
||||
|
||||
/* Glassmorphism and accent classes for key elements */
|
||||
.server-icon, .user-avatar, .member-avatar-small {
|
||||
background: rgba(26,26,26,0.85);
|
||||
backdrop-filter: blur(6px);
|
||||
}
|
||||
|
||||
.channel-item.active, .channel-item:hover, .member-item:hover {
|
||||
background: rgba(26,26,26,0.85);
|
||||
backdrop-filter: blur(4px);
|
||||
}
|
||||
|
||||
.message-input, .message-input-container {
|
||||
background: rgba(15,15,15,0.95);
|
||||
backdrop-filter: blur(4px);
|
||||
}
|
||||
|
||||
/* Hide scrollbars for a cleaner look */
|
||||
::-webkit-scrollbar {
|
||||
width: 8px;
|
||||
background: #111;
|
||||
}
|
||||
::-webkit-scrollbar-thumb {
|
||||
background: #222;
|
||||
border-radius: 4px;
|
||||
}
|
||||
|
||||
/* Utility classes for gradients, badges, etc. can be extended as needed */
|
||||
13
packages/desktop/src/renderer/index.html
Normal file
13
packages/desktop/src/renderer/index.html
Normal file
|
|
@ -0,0 +1,13 @@
|
|||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
<title>AeThex Connect</title>
|
||||
<link rel="stylesheet" href="./global.css" />
|
||||
</head>
|
||||
<body>
|
||||
<div id="root"></div>
|
||||
<script type="module" src="./index.tsx"></script>
|
||||
</body>
|
||||
</html>
|
||||
12
packages/desktop/src/renderer/index.tsx
Normal file
12
packages/desktop/src/renderer/index.tsx
Normal file
|
|
@ -0,0 +1,12 @@
|
|||
import React from "react";
|
||||
import { createRoot } from "react-dom/client";
|
||||
import App from "./App";
|
||||
|
||||
const root = document.getElementById("root");
|
||||
if (root) {
|
||||
createRoot(root).render(
|
||||
<React.StrictMode>
|
||||
<App />
|
||||
</React.StrictMode>
|
||||
);
|
||||
}
|
||||
65
src/frontend/mockup/ChannelSidebar.jsx
Normal file
65
src/frontend/mockup/ChannelSidebar.jsx
Normal file
|
|
@ -0,0 +1,65 @@
|
|||
import React from "react";
|
||||
|
||||
export default function ChannelSidebar() {
|
||||
return (
|
||||
<div className="channel-sidebar w-72 bg-[#0f0f0f] border-r border-[#1a1a1a] flex flex-col">
|
||||
{/* Server Header */}
|
||||
<div className="server-header p-4 border-b border-[#1a1a1a] font-bold text-base flex items-center justify-between">
|
||||
<span>AeThex Foundation</span>
|
||||
<span className="server-badge foundation text-xs px-2 py-1 rounded bg-red-900/20 text-red-500 border border-red-500 uppercase tracking-wider">Official</span>
|
||||
</div>
|
||||
{/* Channel List */}
|
||||
<div className="channel-list flex-1 overflow-y-auto py-2">
|
||||
<div className="channel-category px-4 pt-4 pb-2 text-xs uppercase tracking-widest text-gray-500 font-bold">Announcements</div>
|
||||
<div className="channel-item flex items-center gap-2 px-4 py-2 mx-2 rounded cursor-pointer text-sm hover:bg-[#1a1a1a]">
|
||||
<span className="channel-icon">📢</span>
|
||||
<span className="channel-name flex-1">updates</span>
|
||||
<span className="channel-badge text-xs bg-red-600 text-white px-2 rounded-full">3</span>
|
||||
</div>
|
||||
<div className="channel-item flex items-center gap-2 px-4 py-2 mx-2 rounded cursor-pointer text-sm hover:bg-[#1a1a1a]">
|
||||
<span className="channel-icon">📜</span>
|
||||
<span className="channel-name flex-1">changelog</span>
|
||||
</div>
|
||||
<div className="channel-category px-4 pt-4 pb-2 text-xs uppercase tracking-widest text-gray-500 font-bold">Development</div>
|
||||
<div className="channel-item active flex items-center gap-2 px-4 py-2 mx-2 rounded cursor-pointer text-sm bg-[#1a1a1a]">
|
||||
<span className="channel-icon">#</span>
|
||||
<span className="channel-name flex-1">general</span>
|
||||
</div>
|
||||
<div className="channel-item flex items-center gap-2 px-4 py-2 mx-2 rounded cursor-pointer text-sm hover:bg-[#1a1a1a]">
|
||||
<span className="channel-icon">#</span>
|
||||
<span className="channel-name flex-1">api-discussion</span>
|
||||
</div>
|
||||
<div className="channel-item flex items-center gap-2 px-4 py-2 mx-2 rounded cursor-pointer text-sm hover:bg-[#1a1a1a]">
|
||||
<span className="channel-icon">#</span>
|
||||
<span className="channel-name flex-1">passport-development</span>
|
||||
</div>
|
||||
<div className="channel-category px-4 pt-4 pb-2 text-xs uppercase tracking-widest text-gray-500 font-bold">Support</div>
|
||||
<div className="channel-item flex items-center gap-2 px-4 py-2 mx-2 rounded cursor-pointer text-sm hover:bg-[#1a1a1a]">
|
||||
<span className="channel-icon">❓</span>
|
||||
<span className="channel-name flex-1">help</span>
|
||||
</div>
|
||||
<div className="channel-item flex items-center gap-2 px-4 py-2 mx-2 rounded cursor-pointer text-sm hover:bg-[#1a1a1a]">
|
||||
<span className="channel-icon">🐛</span>
|
||||
<span className="channel-name flex-1">bug-reports</span>
|
||||
</div>
|
||||
<div className="channel-category px-4 pt-4 pb-2 text-xs uppercase tracking-widest text-gray-500 font-bold">Voice Channels</div>
|
||||
<div className="channel-item flex items-center gap-2 px-4 py-2 mx-2 rounded cursor-pointer text-sm hover:bg-[#1a1a1a]">
|
||||
<span className="channel-icon">🔊</span>
|
||||
<span className="channel-name flex-1">Nexus Lounge</span>
|
||||
<span className="text-gray-500 text-xs">3</span>
|
||||
</div>
|
||||
</div>
|
||||
{/* User Presence */}
|
||||
<div className="user-presence p-3 border-t border-[#1a1a1a] flex items-center gap-3 text-sm">
|
||||
<div className="user-avatar w-10 h-10 rounded-full flex items-center justify-center font-bold bg-gradient-to-tr from-red-600 via-blue-600 to-orange-400">A</div>
|
||||
<div className="user-info flex-1">
|
||||
<div className="user-name font-bold mb-0.5">Anderson</div>
|
||||
<div className="user-status flex items-center gap-1 text-xs text-gray-500">
|
||||
<span className="status-dot w-2 h-2 rounded-full bg-green-400 shadow-green-400/50 shadow" />
|
||||
<span>Building AeThex</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
41
src/frontend/mockup/ChatArea.jsx
Normal file
41
src/frontend/mockup/ChatArea.jsx
Normal file
|
|
@ -0,0 +1,41 @@
|
|||
import React from "react";
|
||||
import Message from "./Message";
|
||||
import MessageInput from "./MessageInput";
|
||||
|
||||
const messages = [
|
||||
{ type: "system", label: "FOUNDATION", text: "Foundation authentication services upgraded to v2.1.0. Enhanced security protocols now active across all AeThex infrastructure.", className: "foundation" },
|
||||
{ type: "user", author: "Trevor", badge: "Foundation", time: "10:34 AM", text: "Just pushed the authentication updates. All services should automatically migrate to the new protocols within 24 hours.", avatar: "T", avatarBg: "from-red-600 to-red-800" },
|
||||
{ type: "user", author: "Marcus", time: "10:41 AM", text: "Excellent work! I've been testing the new Passport integration and it's incredibly smooth. The Trinity color-coding in the UI makes it really clear which division is handling what.", avatar: "M", avatarBg: "from-blue-600 to-blue-900" },
|
||||
{ type: "system", label: "LABS", text: "Nexus Engine v2.0-beta now available for testing. New cross-platform sync reduces latency by 40%. Join #labs-testing to participate.", className: "labs" },
|
||||
{ type: "user", author: "Sarah", badge: "Labs", time: "11:15 AM", text: "The Nexus v2 parallel compilation is insane. Cut my build time from 3 minutes to under 2. Still some edge cases with complex state synchronization but wow... ⚠️", avatar: "S", avatarBg: "from-orange-400 to-orange-700" },
|
||||
{ type: "user", author: "Anderson", badge: "Founder", time: "11:47 AM", text: "Love seeing the Trinity infrastructure working in harmony. Foundation keeping everything secure, Labs pushing the boundaries, Corporation delivering production-ready tools. This is exactly the vision.", avatar: "A", avatarBg: "from-red-600 via-blue-600 to-orange-400" },
|
||||
{ type: "user", author: "DevUser_2847", time: "12:03 PM", text: "Quick question - when using AeThex Studio, does the Terminal automatically connect to all three Trinity divisions, or do I need to configure that?", avatar: "D", avatarBg: "bg-[#1a1a1a]" },
|
||||
{ type: "system", label: "CORPORATION", text: "AeThex Studio Pro users: New Railway deployment templates available. Optimized configurations for Foundation APIs, Corporation services, and Labs experiments.", className: "corporation" },
|
||||
];
|
||||
|
||||
export default function ChatArea() {
|
||||
return (
|
||||
<div className="chat-area flex flex-col flex-1 bg-[#0a0a0a]">
|
||||
{/* Chat Header */}
|
||||
<div className="chat-header px-5 py-4 border-b border-[#1a1a1a] flex items-center gap-3">
|
||||
<span className="channel-name-header flex-1 font-bold text-base"># general</span>
|
||||
<div className="chat-tools flex gap-4 text-sm text-gray-500">
|
||||
<span className="chat-tool cursor-pointer hover:text-blue-500">🔔</span>
|
||||
<span className="chat-tool cursor-pointer hover:text-blue-500">📌</span>
|
||||
<span className="chat-tool cursor-pointer hover:text-blue-500">👥 128</span>
|
||||
<span className="chat-tool cursor-pointer hover:text-blue-500">🔍</span>
|
||||
</div>
|
||||
</div>
|
||||
{/* Messages */}
|
||||
<div className="chat-messages flex-1 overflow-y-auto px-5 py-5">
|
||||
{messages.map((msg, i) => (
|
||||
<Message key={i} {...msg} />
|
||||
))}
|
||||
</div>
|
||||
{/* Message Input */}
|
||||
<div className="message-input-container px-5 py-5 border-t border-[#1a1a1a]">
|
||||
<MessageInput />
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
17
src/frontend/mockup/MainLayout.jsx
Normal file
17
src/frontend/mockup/MainLayout.jsx
Normal file
|
|
@ -0,0 +1,17 @@
|
|||
import React from "react";
|
||||
import ServerList from "./ServerList";
|
||||
import ChannelSidebar from "./ChannelSidebar";
|
||||
import ChatArea from "./ChatArea";
|
||||
import MemberSidebar from "./MemberSidebar";
|
||||
import "./global.css";
|
||||
|
||||
export default function MainLayout() {
|
||||
return (
|
||||
<div className="connect-container flex h-screen">
|
||||
<ServerList />
|
||||
<ChannelSidebar />
|
||||
<ChatArea />
|
||||
<MemberSidebar />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
43
src/frontend/mockup/MemberSidebar.jsx
Normal file
43
src/frontend/mockup/MemberSidebar.jsx
Normal file
|
|
@ -0,0 +1,43 @@
|
|||
import React from "react";
|
||||
|
||||
const members = [
|
||||
{ section: "Foundation Team — 8", users: [
|
||||
{ name: "Anderson", avatar: "A", status: "online", avatarBg: "from-red-600 to-red-800" },
|
||||
{ name: "Trevor", avatar: "T", status: "online", avatarBg: "from-red-600 to-red-800" },
|
||||
]},
|
||||
{ section: "Labs Team — 12", users: [
|
||||
{ name: "Sarah", avatar: "S", status: "labs", avatarBg: "from-orange-400 to-orange-700", activity: "Testing v2.0" },
|
||||
]},
|
||||
{ section: "Developers — 47", users: [
|
||||
{ name: "Marcus", avatar: "M", status: "in-game", avatarBg: "bg-[#1a1a1a]", activity: "Building" },
|
||||
{ name: "DevUser_2847", avatar: "D", status: "online", avatarBg: "bg-[#1a1a1a]" },
|
||||
]},
|
||||
{ section: "Community — 61", users: [
|
||||
{ name: "JohnDev", avatar: "J", status: "offline", avatarBg: "bg-[#1a1a1a]" },
|
||||
]},
|
||||
];
|
||||
|
||||
export default function MemberSidebar() {
|
||||
return (
|
||||
<div className="member-sidebar w-72 bg-[#0f0f0f] border-l border-[#1a1a1a] flex flex-col">
|
||||
<div className="member-header p-4 border-b border-[#1a1a1a] text-xs uppercase tracking-widest text-gray-500">Members — 128</div>
|
||||
<div className="member-list flex-1 overflow-y-auto py-3">
|
||||
{members.map((section, i) => (
|
||||
<div key={i} className="member-section mb-4">
|
||||
<div className="member-section-title px-4 py-2 text-xs uppercase tracking-wider text-gray-500 font-bold">{section.section}</div>
|
||||
{section.users.map((user, j) => (
|
||||
<div key={j} className="member-item flex items-center gap-3 px-4 py-1.5 cursor-pointer hover:bg-[#1a1a1a]">
|
||||
<div className={`member-avatar-small w-8 h-8 rounded-full flex items-center justify-center font-bold text-sm relative bg-gradient-to-tr ${user.avatarBg}`}>
|
||||
{user.avatar}
|
||||
<div className={`online-indicator absolute -bottom-0.5 -right-0.5 w-3 h-3 rounded-full border-2 border-[#0f0f0f] ${user.status === "online" ? "bg-green-400" : user.status === "in-game" ? "bg-blue-500" : user.status === "labs" ? "bg-orange-400" : "bg-gray-700"}`}></div>
|
||||
</div>
|
||||
<div className="member-name flex-1 text-sm">{user.name}</div>
|
||||
{user.activity && <div className="member-activity text-xs text-gray-500">{user.activity}</div>}
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
27
src/frontend/mockup/Message.jsx
Normal file
27
src/frontend/mockup/Message.jsx
Normal file
|
|
@ -0,0 +1,27 @@
|
|||
import React from "react";
|
||||
|
||||
export default function Message(props) {
|
||||
if (props.type === "system") {
|
||||
return (
|
||||
<div className={`message-system ${props.className} bg-[#0f0f0f] border-l-4 pl-4 pr-4 py-3 mb-4 text-sm`}>
|
||||
<div className={`system-label ${props.className} text-xs uppercase tracking-wider font-bold mb-1`}>[{props.label}] System Announcement</div>
|
||||
<div>{props.text}</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
return (
|
||||
<div className="message flex gap-4 mb-5 p-3 rounded transition hover:bg-[#0f0f0f]">
|
||||
<div className={`message-avatar w-10 h-10 rounded-full flex items-center justify-center font-bold text-base flex-shrink-0 bg-gradient-to-tr ${props.avatarBg}`}>{props.avatar}</div>
|
||||
<div className="message-content flex-1">
|
||||
<div className="message-header flex items-baseline gap-3 mb-1">
|
||||
<span className="message-author font-bold">{props.author}</span>
|
||||
{props.badge && (
|
||||
<span className={`message-badge ${props.className} text-xs px-2 py-1 rounded uppercase tracking-wider font-bold`}>{props.badge}</span>
|
||||
)}
|
||||
<span className="message-time text-xs text-gray-500">{props.time}</span>
|
||||
</div>
|
||||
<div className="message-text leading-relaxed text-gray-300">{props.text}</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
16
src/frontend/mockup/MessageInput.jsx
Normal file
16
src/frontend/mockup/MessageInput.jsx
Normal file
|
|
@ -0,0 +1,16 @@
|
|||
import React from "react";
|
||||
|
||||
export default function MessageInput() {
|
||||
return (
|
||||
<div className="flex items-center gap-2">
|
||||
<button className="attachButton w-10 h-10 flex items-center justify-center rounded bg-[#1a1a1a] text-xl text-gray-400 mr-2">+</button>
|
||||
<input
|
||||
type="text"
|
||||
className="message-input flex-1 bg-[#0f0f0f] border border-[#1a1a1a] rounded-lg px-4 py-3 text-gray-200 text-sm focus:outline-none focus:border-blue-500 placeholder:text-gray-500"
|
||||
placeholder="Message #general (Foundation infrastructure channel)"
|
||||
maxLength={2000}
|
||||
/>
|
||||
<button className="sendButton w-10 h-10 flex items-center justify-center rounded bg-blue-600 text-xl text-white ml-2">🎤</button>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
30
src/frontend/mockup/ServerList.jsx
Normal file
30
src/frontend/mockup/ServerList.jsx
Normal file
|
|
@ -0,0 +1,30 @@
|
|||
import React from "react";
|
||||
|
||||
const servers = [
|
||||
{ id: "foundation", label: "F", active: true, className: "foundation" },
|
||||
{ id: "corporation", label: "C", active: false, className: "corporation" },
|
||||
{ id: "labs", label: "L", active: false, className: "labs" },
|
||||
{ id: "divider" },
|
||||
{ id: "community1", label: "AG", active: false, className: "community" },
|
||||
{ id: "community2", label: "RD", active: false, className: "community" },
|
||||
{ id: "add", label: "+", active: false, className: "community" },
|
||||
];
|
||||
|
||||
export default function ServerList() {
|
||||
return (
|
||||
<div className="server-list flex flex-col items-center py-3 gap-3 w-20 bg-[#0d0d0d] border-r border-[#1a1a1a]">
|
||||
{servers.map((srv, i) =>
|
||||
srv.id === "divider" ? (
|
||||
<div key={i} className="server-divider w-10 h-0.5 bg-[#1a1a1a] my-1" />
|
||||
) : (
|
||||
<div
|
||||
key={srv.id}
|
||||
className={`server-icon ${srv.className} ${srv.active ? "active" : ""} w-14 h-14 rounded-xl flex items-center justify-center font-bold text-lg cursor-pointer transition-all relative`}
|
||||
>
|
||||
{srv.label}
|
||||
</div>
|
||||
)
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
57
src/frontend/mockup/global.css
Normal file
57
src/frontend/mockup/global.css
Normal file
|
|
@ -0,0 +1,57 @@
|
|||
@import url('https://fonts.googleapis.com/css2?family=Roboto+Mono:wght@300;400;500;700&display=swap');
|
||||
|
||||
html, body, #root {
|
||||
height: 100%;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
font-family: 'Roboto Mono', monospace;
|
||||
background: #0a0a0a;
|
||||
color: #e0e0e0;
|
||||
}
|
||||
|
||||
body::before {
|
||||
content: '';
|
||||
position: fixed;
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 100vw;
|
||||
height: 100vh;
|
||||
background: repeating-linear-gradient(
|
||||
0deg,
|
||||
rgba(0, 0, 0, 0.15),
|
||||
rgba(0, 0, 0, 0.15) 1px,
|
||||
transparent 1px,
|
||||
transparent 2px
|
||||
);
|
||||
pointer-events: none;
|
||||
z-index: 1000;
|
||||
}
|
||||
|
||||
.connect-container {
|
||||
height: 100vh;
|
||||
display: flex;
|
||||
}
|
||||
|
||||
.server-icon, .user-avatar, .member-avatar-small {
|
||||
background: rgba(26,26,26,0.85);
|
||||
backdrop-filter: blur(6px);
|
||||
}
|
||||
|
||||
.channel-item.active, .channel-item:hover, .member-item:hover {
|
||||
background: rgba(26,26,26,0.85);
|
||||
backdrop-filter: blur(4px);
|
||||
}
|
||||
|
||||
.message-input, .message-input-container {
|
||||
background: rgba(15,15,15,0.95);
|
||||
backdrop-filter: blur(4px);
|
||||
}
|
||||
|
||||
::-webkit-scrollbar {
|
||||
width: 8px;
|
||||
background: #111;
|
||||
}
|
||||
::-webkit-scrollbar-thumb {
|
||||
background: #222;
|
||||
border-radius: 4px;
|
||||
}
|
||||
13
src/frontend/mockup/index.html
Normal file
13
src/frontend/mockup/index.html
Normal file
|
|
@ -0,0 +1,13 @@
|
|||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
<title>AeThex Connect Mockup Web</title>
|
||||
<link rel="stylesheet" href="./global.css" />
|
||||
</head>
|
||||
<body>
|
||||
<div id="root"></div>
|
||||
<script type="module" src="./index.jsx"></script>
|
||||
</body>
|
||||
</html>
|
||||
13
src/frontend/mockup/index.jsx
Normal file
13
src/frontend/mockup/index.jsx
Normal file
|
|
@ -0,0 +1,13 @@
|
|||
import React from "react";
|
||||
import { createRoot } from "react-dom/client";
|
||||
import MainLayout from "./MainLayout";
|
||||
import "./global.css";
|
||||
|
||||
const root = document.getElementById("root");
|
||||
if (root) {
|
||||
createRoot(root).render(
|
||||
<React.StrictMode>
|
||||
<MainLayout />
|
||||
</React.StrictMode>
|
||||
);
|
||||
}
|
||||
Loading…
Reference in a new issue