C50BARZ commited on
Commit
69c6940
·
verified ·
1 Parent(s): dd702b2

Build a retro chat app

Browse files
Files changed (6) hide show
  1. README.md +7 -4
  2. components/nav-bar.js +187 -0
  3. components/side-bar.js +151 -0
  4. index.html +67 -19
  5. script.js +336 -0
  6. style.css +265 -19
README.md CHANGED
@@ -1,10 +1,13 @@
1
  ---
2
- title: Chatverse Retro
3
- emoji: 🔥
4
- colorFrom: blue
5
  colorTo: yellow
 
6
  sdk: static
7
  pinned: false
 
 
8
  ---
9
 
10
- Check out the configuration reference at https://huggingface.co/docs/hub/spaces-config-reference
 
 
1
  ---
2
+ title: ChatVerse Retro 💬🕹️
3
+ colorFrom: purple
 
4
  colorTo: yellow
5
+ emoji: 🐳
6
  sdk: static
7
  pinned: false
8
+ tags:
9
+ - deepsite-v3
10
  ---
11
 
12
+ # Welcome to your new DeepSite project!
13
+ This project was created with [DeepSite](https://huggingface.co/deepsite).
components/nav-bar.js ADDED
@@ -0,0 +1,187 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ class NavBar extends HTMLElement {
2
+ constructor() {
3
+ super();
4
+ this.currentUser = localStorage.getItem('currentUser') || 'Anonymous';
5
+ }
6
+
7
+ connectedCallback() {
8
+ this.attachShadow({ mode: 'open' });
9
+ this.render();
10
+ this.setupEventListeners();
11
+ }
12
+
13
+ render() {
14
+ this.shadowRoot.innerHTML = `
15
+ <style>
16
+ :host {
17
+ position: fixed;
18
+ top: 0;
19
+ left: 0;
20
+ right: 0;
21
+ z-index: 100;
22
+ }
23
+
24
+ .navbar {
25
+ background: var(--bg-darker, #050505);
26
+ border-bottom: 2px solid var(--border-color, #00ff00);
27
+ padding: 0.5rem 1rem;
28
+ display: flex;
29
+ justify-content: space-between;
30
+ align-items: center;
31
+ box-shadow: 0 2px 20px var(--primary, #00ff00);
32
+ position: relative;
33
+ overflow: hidden;
34
+ }
35
+
36
+ .navbar::before {
37
+ content: '';
38
+ position: absolute;
39
+ top: 0;
40
+ left: -100%;
41
+ width: 100%;
42
+ height: 100%;
43
+ background: linear-gradient(90deg, transparent, rgba(0, 255, 0, 0.1), transparent);
44
+ animation: scan 3s infinite;
45
+ }
46
+
47
+ @keyframes scan {
48
+ 0% { left: -100%; }
49
+ 100% { left: 100%; }
50
+ }
51
+
52
+ .logo {
53
+ font-family: 'Press Start 2P', cursive;
54
+ font-size: 0.75rem;
55
+ color: var(--primary, #00ff00);
56
+ text-shadow: 0 0 10px var(--primary, #00ff00);
57
+ display: flex;
58
+ align-items: center;
59
+ gap: 0.5rem;
60
+ }
61
+
62
+ .nav-links {
63
+ display: flex;
64
+ gap: 1rem;
65
+ align-items: center;
66
+ }
67
+
68
+ .nav-link {
69
+ color: var(--text-secondary, #a0a0a0);
70
+ text-decoration: none;
71
+ font-family: 'Press Start 2P', cursive;
72
+ font-size: 0.5rem;
73
+ padding: 0.5rem;
74
+ border: 1px solid transparent;
75
+ transition: all 0.3s;
76
+ position: relative;
77
+ }
78
+
79
+ .nav-link:hover {
80
+ color: var(--primary, #00ff00);
81
+ border-color: var(--primary, #00ff00);
82
+ box-shadow: 0 0 10px var(--primary, #00ff00);
83
+ }
84
+
85
+ .nav-link.active {
86
+ color: var(--primary, #00ff00);
87
+ border-color: var(--primary, #00ff00);
88
+ }
89
+
90
+ .user-info {
91
+ display: flex;
92
+ align-items: center;
93
+ gap: 0.5rem;
94
+ font-family: 'Press Start 2P', cursive;
95
+ font-size: 0.5rem;
96
+ color: var(--text-primary, #00ff00);
97
+ }
98
+
99
+ .status-indicator {
100
+ width: 0.5rem;
101
+ height: 0.5rem;
102
+ background: var(--primary, #00ff00);
103
+ border-radius: 50%;
104
+ box-shadow: 0 0 10px var(--primary, #00ff00);
105
+ animation: pulse 2s infinite;
106
+ }
107
+
108
+ @keyframes pulse {
109
+ 0%, 100% { opacity: 1; }
110
+ 50% { opacity: 0.5; }
111
+ }
112
+
113
+ .menu-toggle {
114
+ display: none;
115
+ background: none;
116
+ border: 1px solid var(--primary, #00ff00);
117
+ color: var(--primary, #00ff00);
118
+ padding: 0.25rem;
119
+ cursor: pointer;
120
+ }
121
+
122
+ .menu-toggle:hover {
123
+ box-shadow: 0 0 10px var(--primary, #00ff00);
124
+ }
125
+
126
+ @media (max-width: 768px) {
127
+ .nav-links {
128
+ display: none;
129
+ }
130
+
131
+ .menu-toggle {
132
+ display: block;
133
+ }
134
+
135
+ .logo {
136
+ font-size: 0.6rem;
137
+ }
138
+ }
139
+ </style>
140
+
141
+ <nav class="navbar">
142
+ <div class="logo">
143
+ <i data-feather="message-square" width="16" height="16"></i>
144
+ CHATVERSE RETRO
145
+ </div>
146
+
147
+ <div class="nav-links">
148
+ <a href="index.html" class="nav-link active">CHAT</a>
149
+ <a href="profile.html" class="nav-link">PROFILE</a>
150
+ <a href="login.html" class="nav-link">LOGOUT</a>
151
+ </div>
152
+
153
+ <div class="user-info">
154
+ <div class="status-indicator"></div>
155
+ <span id="currentUser">${this.currentUser}</span>
156
+ </div>
157
+
158
+ <button class="menu-toggle" id="menuToggle">
159
+ <i data-feather="menu" width="16" height="16"></i>
160
+ </button>
161
+ </nav>
162
+ `;
163
+ }
164
+
165
+ setupEventListeners() {
166
+ // Update current user when it changes
167
+ document.addEventListener('userChanged', (e) => {
168
+ this.currentUser = e.detail.user;
169
+ const userElement = this.shadowRoot.getElementById('currentUser');
170
+ if (userElement) {
171
+ userElement.textContent = this.currentUser;
172
+ }
173
+ });
174
+
175
+ // Handle navigation
176
+ const navLinks = this.shadowRoot.querySelectorAll('.nav-link');
177
+ navLinks.forEach(link => {
178
+ link.addEventListener('click', (e) => {
179
+ // Update active state
180
+ navLinks.forEach(l => l.classList.remove('active'));
181
+ link.classList.add('active');
182
+ });
183
+ });
184
+ }
185
+ }
186
+
187
+ customElements.define('nav-bar', NavBar);
components/side-bar.js ADDED
@@ -0,0 +1,151 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ class SideBar extends HTMLElement {
2
+ constructor() {
3
+ super();
4
+ this.contacts = [];
5
+ this.isMobile = window.innerWidth <= 768;
6
+ this.isCollapsed = this.isMobile;
7
+ }
8
+
9
+ connectedCallback() {
10
+ this.attachShadow({ mode: 'open' });
11
+ this.render();
12
+ this.setupEventListeners();
13
+ this.loadContacts();
14
+ }
15
+
16
+ render() {
17
+ this.shadowRoot.innerHTML = `
18
+ <style>
19
+ :host {
20
+ height: calc(100vh - 4rem);
21
+ position: fixed;
22
+ left: 0;
23
+ top: 4rem;
24
+ z-index: 50;
25
+ transition: transform 0.3s ease;
26
+ }
27
+
28
+ .sidebar {
29
+ width: 280px;
30
+ height: 100%;
31
+ background: var(--bg-dark, #000000);
32
+ border-right: 1px solid var(--border-color, #00ff00);
33
+ display: flex;
34
+ flex-direction: column;
35
+ overflow: hidden;
36
+ box-shadow: 2px 0 20px rgba(0, 255, 0, 0.2);
37
+ }
38
+
39
+ .sidebar-header {
40
+ padding: 1rem;
41
+ border-bottom: 1px solid var(--border-color, #00ff00);
42
+ background: var(--bg-darker, #050505);
43
+ }
44
+
45
+ .sidebar-title {
46
+ font-family: 'Press Start 2P', cursive;
47
+ font-size: 0.6rem;
48
+ color: var(--primary, #00ff00);
49
+ margin-bottom: 0.5rem;
50
+ }
51
+
52
+ .online-count {
53
+ font-family: monospace;
54
+ font-size: 0.8rem;
55
+ color: var(--text-secondary, #a0a0a0);
56
+ }
57
+
58
+ .contacts-list {
59
+ flex: 1;
60
+ overflow-y: auto;
61
+ padding: 0.5rem;
62
+ }
63
+
64
+ .contact-item {
65
+ display: flex;
66
+ align-items: center;
67
+ gap: 0.75rem;
68
+ padding: 0.75rem;
69
+ border: 1px solid transparent;
70
+ cursor: pointer;
71
+ transition: all 0.3s;
72
+ margin-bottom: 0.25rem;
73
+ }
74
+
75
+ .contact-item:hover {
76
+ background: var(--bg-light, #1a1a1a);
77
+ border-color: var(--primary, #00ff00);
78
+ box-shadow: 0 0 10px rgba(0, 255, 0, 0.1);
79
+ }
80
+
81
+ .contact-item.active {
82
+ background: var(--bg-light, #1a1a1a);
83
+ border-color: var(--primary, #00ff00);
84
+ }
85
+
86
+ .contact-avatar {
87
+ width: 40px;
88
+ height: 40px;
89
+ border-radius: 4px;
90
+ background: var(--bg-light, #1a1a1a);
91
+ border: 1px solid var(--border-color, #00ff00);
92
+ display: flex;
93
+ align-items: center;
94
+ justify-content: center;
95
+ font-size: 1.5rem;
96
+ }
97
+
98
+ .contact-info {
99
+ flex: 1;
100
+ min-width: 0;
101
+ }
102
+
103
+ .contact-name {
104
+ font-family: 'Press Start 2P', cursive;
105
+ font-size: 0.5rem;
106
+ color: var(--text-primary, #00ff00);
107
+ margin-bottom: 0.25rem;
108
+ }
109
+
110
+ .contact-status {
111
+ font-family: monospace;
112
+ font-size: 0.7rem;
113
+ color: var(--text-muted, #505050);
114
+ }
115
+
116
+ .status-indicator {
117
+ width: 8px;
118
+ height: 8px;
119
+ border-radius: 50%;
120
+ flex-shrink: 0;
121
+ }
122
+
123
+ .status-online {
124
+ background: var(--primary, #00ff00);
125
+ box-shadow: 0 0 10px var(--primary, #00ff00);
126
+ }
127
+
128
+ .status-away {
129
+ background: #ffaa00;
130
+ box-shadow: 0 0 10px #ffaa00;
131
+ }
132
+
133
+ .status-offline {
134
+ background: var(--text-muted, #505050);
135
+ }
136
+
137
+ .contact-last-message {
138
+ font-family: monospace;
139
+ font-size: 0.7rem;
140
+ color: var(--text-secondary, #a0a0a0);
141
+ white-space: nowrap;
142
+ overflow: hidden;
143
+ text-overflow: ellipsis;
144
+ }
145
+
146
+ .add-contact-btn {
147
+ width: 100%;
148
+ padding: 0.75rem;
149
+ background: var(--bg-light, #1a1a1a);
150
+ border: 1px solid var(--border-color, #00ff00);
151
+ color: var(--primary, #00ff00);
index.html CHANGED
@@ -1,19 +1,67 @@
1
- <!doctype html>
2
- <html>
3
- <head>
4
- <meta charset="utf-8" />
5
- <meta name="viewport" content="width=device-width" />
6
- <title>My static Space</title>
7
- <link rel="stylesheet" href="style.css" />
8
- </head>
9
- <body>
10
- <div class="card">
11
- <h1>Welcome to your static Space!</h1>
12
- <p>You can modify this app directly by editing <i>index.html</i> in the Files and versions tab.</p>
13
- <p>
14
- Also don't forget to check the
15
- <a href="https://huggingface.co/docs/hub/spaces" target="_blank">Spaces documentation</a>.
16
- </p>
17
- </div>
18
- </body>
19
- </html>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <!DOCTYPE html>
2
+ <html lang="en" data-theme="dark">
3
+ <head>
4
+ <meta charset="UTF-8">
5
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
6
+ <title>ChatVerse Retro - Main Chat</title>
7
+ <link rel="icon" type="image/x-icon" href="data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='%2300ff00'%3E%3Cpath d='M21 11.5a8.38 8.38 0 0 1-.9 3.8 8.5 8.5 0 0 1-7.6 4.7 8.38 8.38 0 0 1-3.8-.9L3 21l1.9-5.7a8.38 8.38 0 0 1-.9-3.8 8.5 8.5 0 0 1 4.7-7.6 8.38 8.38 0 0 1 3.8-.9h.5a8.48 8.48 0 0 1 8 8v.5z'/%3E%3C/svg%3E">
8
+ <link rel="preconnect" href="https://fonts.googleapis.com">
9
+ <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
10
+ <link href="https://fonts.googleapis.com/css2?family=Press+Start+2P&display=swap" rel="stylesheet">
11
+ <script src="https://cdn.tailwindcss.com"></script>
12
+ <script src="https://cdn.jsdelivr.net/npm/feather-icons/dist/feather.min.js"></script>
13
+ <script src="https://unpkg.com/feather-icons"></script>
14
+ <link rel="stylesheet" href="style.css">
15
+ </head>
16
+ <body class="retro-bg min-h-screen">
17
+ <div class="crt-overlay"></div>
18
+ <div class="scanlines"></div>
19
+
20
+ <nav-bar></nav-bar>
21
+
22
+ <div class="flex h-screen pt-16">
23
+ <side-bar></side-bar>
24
+
25
+ <main class="flex-1 flex flex-col bg-black/40 backdrop-blur-sm border-t border-l border-emerald-400/30 shadow-inner shadow-emerald-400/20">
26
+ <div class="flex-1 overflow-y-auto p-4 space-y-4 messages-container" id="messagesContainer">
27
+ <div class="message-group">
28
+ <div class="message-header flex items-center gap-2 mb-2">
29
+ <div class="w-3 h-3 bg-emerald-400 shadow-[0_0_10px_#00ff00]"></div>
30
+ <span class="font-['Press_Start_2P'] text-xs text-emerald-400">SYSTEM</span>
31
+ <span class="text-slate-500 text-xs" data-timestamp="new">[NOW]</span>
32
+ </div>
33
+ <div class="message-content font-mono text-slate-300 text-sm leading-relaxed">
34
+ > Welcome to ChatVerse Retro v1.0<br>
35
+ > Type /help for available commands<br>
36
+ > Connection established: ONLINE
37
+ </div>
38
+ </div>
39
+ </div>
40
+
41
+ <div class="border-t border-emerald-400/30 p-4 bg-black/60">
42
+ <div class="flex gap-2">
43
+ <input
44
+ type="text"
45
+ id="messageInput"
46
+ placeholder="> Type your message..."
47
+ class="flex-1 bg-black/80 border border-emerald-400/50 text-emerald-400 font-mono text-sm px-4 py-3 focus:outline-none focus:border-emerald-400 focus:shadow-[0_0_15px_#00ff00] transition-all duration-300 placeholder:text-emerald-400/50"
48
+ autocomplete="off"
49
+ >
50
+ <button
51
+ id="sendButton"
52
+ class="bg-emerald-400 text-black font-['Press_Start_2P'] text-xs px-6 py-3 hover:bg-emerald-500 hover:shadow-[0_0_20px_#00ff00] transition-all duration-300 active:translate-y-1"
53
+ >
54
+ SEND
55
+ </button>
56
+ </div>
57
+ </div>
58
+ </main>
59
+ </div>
60
+
61
+ <script src="components/nav-bar.js"></script>
62
+ <script src="components/side-bar.js"></script>
63
+ <script src="script.js"></script>
64
+ <script>feather.replace();</script>
65
+ <script src="https://huggingface.co/deepsite/deepsite-badge.js"></script>
66
+ </body>
67
+ </html>
script.js ADDED
@@ -0,0 +1,336 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ // ChatVerse Retro - Main JavaScript
2
+ class ChatApp {
3
+ constructor() {
4
+ this.currentUser = localStorage.getItem('currentUser') || 'Anonymous';
5
+ this.messages = JSON.parse(localStorage.getItem('messages')) || [];
6
+ this.contacts = JSON.parse(localStorage.getItem('contacts')) || this.getDefaultContacts();
7
+ this.activeContact = this.contacts[0];
8
+ this.commands = {
9
+ '/help': this.showHelp.bind(this),
10
+ '/clear': this.clearChat.bind(this),
11
+ '/nick': this.changeNick.bind(this),
12
+ '/theme': this.toggleTheme.bind(this),
13
+ '/time': this.showTime.bind(this),
14
+ '/whoami': this.showCurrentUser.bind(this),
15
+ };
16
+
17
+ this.init();
18
+ }
19
+
20
+ init() {
21
+ this.setupEventListeners();
22
+ this.loadMessages();
23
+ this.updateContacts();
24
+ this.setupTheme();
25
+ this.setupMobile();
26
+ }
27
+
28
+ setupEventListeners() {
29
+ const messageInput = document.getElementById('messageInput');
30
+ const sendButton = document.getElementById('sendButton');
31
+
32
+ if (messageInput) {
33
+ messageInput.addEventListener('keypress', (e) => {
34
+ if (e.key === 'Enter') {
35
+ this.sendMessage();
36
+ }
37
+ });
38
+ }
39
+
40
+ if (sendButton) {
41
+ sendButton.addEventListener('click', () => this.sendMessage());
42
+ }
43
+
44
+ // Auto-resize input
45
+ if (messageInput) {
46
+ messageInput.addEventListener('input', () => {
47
+ messageInput.style.height = 'auto';
48
+ messageInput.style.height = Math.min(messageInput.scrollHeight, 120) + 'px';
49
+ });
50
+ }
51
+ }
52
+
53
+ setupTheme() {
54
+ const savedTheme = localStorage.getItem('theme') || 'dark';
55
+ document.documentElement.setAttribute('data-theme', savedTheme);
56
+ }
57
+
58
+ setupMobile() {
59
+ // Handle mobile menu toggle
60
+ const menuToggle = document.getElementById('menuToggle');
61
+ const sidebar = document.querySelector('side-bar');
62
+
63
+ if (menuToggle && sidebar) {
64
+ menuToggle.addEventListener('click', () => {
65
+ sidebar.classList.toggle('sidebar-expanded');
66
+ sidebar.classList.toggle('sidebar-collapsed');
67
+ });
68
+ }
69
+
70
+ // Close sidebar on outside click for mobile
71
+ document.addEventListener('click', (e) => {
72
+ if (window.innerWidth <= 768) {
73
+ const sidebarElement = document.querySelector('side-bar');
74
+ const isClickInsideSidebar = sidebarElement.contains(e.target);
75
+ const isMenuToggle = menuToggle && menuToggle.contains(e.target);
76
+
77
+ if (!isClickInsideSidebar && !isMenuToggle) {
78
+ sidebarElement.classList.add('sidebar-collapsed');
79
+ sidebarElement.classList.remove('sidebar-expanded');
80
+ }
81
+ }
82
+ });
83
+ }
84
+
85
+ getDefaultContacts() {
86
+ return [
87
+ { id: 1, name: 'System', status: 'online', avatar: '💻', lastMessage: 'Welcome to ChatVerse!' },
88
+ { id: 2, name: 'RetroBot', status: 'online', avatar: '🤖', lastMessage: 'Type /help for commands' },
89
+ { id: 3, name: 'CyberPunk', status: 'away', avatar: '🎮', lastMessage: 'Gaming...' },
90
+ { id: 4, name: 'NeonDream', status: 'offline', avatar: '🌟', lastMessage: 'See you later!' },
91
+ { id: 5, name: 'PixelMaster', status: 'online', avatar: '🎨', lastMessage: 'Check out my new pixel art!' },
92
+ ];
93
+ }
94
+
95
+ sendMessage() {
96
+ const input = document.getElementById('messageInput');
97
+ const content = input.value.trim();
98
+
99
+ if (!content) return;
100
+
101
+ // Check for commands
102
+ if (content.startsWith('/')) {
103
+ this.executeCommand(content);
104
+ input.value = '';
105
+ return;
106
+ }
107
+
108
+ const message = {
109
+ id: Date.now(),
110
+ content: content,
111
+ sender: this.currentUser,
112
+ timestamp: new Date(),
113
+ type: 'user'
114
+ };
115
+
116
+ this.messages.push(message);
117
+ this.saveMessages();
118
+ this.displayMessage(message);
119
+
120
+ // Simulate bot response
121
+ this.simulateBotResponse(content);
122
+
123
+ input.value = '';
124
+ input.style.height = 'auto';
125
+ }
126
+
127
+ executeCommand(command) {
128
+ const parts = command.split(' ');
129
+ const cmd = parts[0].toLowerCase();
130
+ const args = parts.slice(1);
131
+
132
+ if (this.commands[cmd]) {
133
+ this.commands[cmd](args);
134
+ } else {
135
+ this.displaySystemMessage(`Unknown command: ${cmd}. Type /help for available commands.`);
136
+ }
137
+ }
138
+
139
+ showHelp() {
140
+ const helpText = `
141
+ Available Commands:
142
+ /help - Show this help message
143
+ /clear - Clear the chat history
144
+ /nick [name] - Change your nickname
145
+ /theme - Toggle light/dark theme
146
+ /time - Show current server time
147
+ /whoami - Show current user info
148
+ `;
149
+ this.displaySystemMessage(helpText);
150
+ }
151
+
152
+ clearChat() {
153
+ this.messages = [];
154
+ localStorage.removeItem('messages');
155
+ const container = document.getElementById('messagesContainer');
156
+ if (container) {
157
+ container.innerHTML = '';
158
+ }
159
+ this.displaySystemMessage('Chat history cleared.');
160
+ }
161
+
162
+ changeNick(args) {
163
+ const newNick = args.join(' ');
164
+ if (newNick && newNick.length > 0) {
165
+ const oldNick = this.currentUser;
166
+ this.currentUser = newNick;
167
+ localStorage.setItem('currentUser', newNick);
168
+ this.displaySystemMessage(`Nickname changed from "${oldNick}" to "${newNick}"`);
169
+ } else {
170
+ this.displaySystemMessage('Usage: /nick [new nickname]');
171
+ }
172
+ }
173
+
174
+ toggleTheme() {
175
+ const currentTheme = document.documentElement.getAttribute('data-theme');
176
+ const newTheme = currentTheme === 'dark' ? 'light' : 'dark';
177
+ document.documentElement.setAttribute('data-theme', newTheme);
178
+ localStorage.setItem('theme', newTheme);
179
+ this.displaySystemMessage(`Theme switched to ${newTheme} mode`);
180
+ }
181
+
182
+ showTime() {
183
+ const now = new Date();
184
+ this.displaySystemMessage(`Server Time: ${now.toLocaleString()}`);
185
+ }
186
+
187
+ showCurrentUser() {
188
+ this.displaySystemMessage(`Current User: ${this.currentUser}\nTheme: ${document.documentElement.getAttribute('data-theme')} mode`);
189
+ }
190
+
191
+ simulateBotResponse(userMessage) {
192
+ setTimeout(() => {
193
+ const responses = [
194
+ `I see you said: "${userMessage}"`,
195
+ `That's interesting! Tell me more.`,
196
+ `🤖 Processing... Processing...`,
197
+ `Beep boop! Message received.`,
198
+ `You sound like a real retro gamer!`,
199
+ `404: Witty response not found 😄`,
200
+ `I'm just a bot, but I appreciate the message!`,
201
+ `System: Message delivered successfully.`
202
+ ];
203
+
204
+ const randomResponse = responses[Math.floor(Math.random() * responses.length)];
205
+
206
+ const botMessage = {
207
+ id: Date.now() + 1,
208
+ content: randomResponse,
209
+ sender: 'RetroBot',
210
+ timestamp: new Date(),
211
+ type: 'bot'
212
+ };
213
+
214
+ this.messages.push(botMessage);
215
+ this.saveMessages();
216
+ this.displayMessage(botMessage);
217
+ }, Math.random() * 2000 + 1000);
218
+ }
219
+
220
+ displayMessage(message) {
221
+ const container = document.getElementById('messagesContainer');
222
+ if (!container) return;
223
+
224
+ const messageDiv = document.createElement('div');
225
+ messageDiv.className = 'message-group';
226
+
227
+ const timestamp = this.formatTime(message.timestamp);
228
+ const senderClass = message.type === 'user' ? 'text-emerald-400' : 'text-cyan-400';
229
+ const senderGlow = message.type === 'user' ? 'shadow-[0_0_10px_#00ff00]' : 'shadow-[0_0_10px_#00ffff]';
230
+
231
+ messageDiv.innerHTML = `
232
+ <div class="message-header flex items-center gap-2 mb-2">
233
+ <div class="w-3 h-3 ${senderClass} ${senderGlow}"></div>
234
+ <span class="font-['Press_Start_2P'] text-xs ${senderClass}">${message.sender}</span>
235
+ <span class="text-slate-500 text-xs" data-timestamp="${timestamp}">[${timestamp}]</span>
236
+ </div>
237
+ <div class="message-content font-mono text-slate-300 text-sm leading-relaxed">
238
+ > ${this.escapeHtml(message.content)}
239
+ </div>
240
+ `;
241
+
242
+ container.appendChild(messageDiv);
243
+ this.scrollToBottom();
244
+ }
245
+
246
+ displaySystemMessage(content) {
247
+ const message = {
248
+ id: Date.now(),
249
+ content: content,
250
+ sender: 'SYSTEM',
251
+ timestamp: new Date(),
252
+ type: 'system'
253
+ };
254
+ this.displayMessage(message);
255
+ }
256
+
257
+ loadMessages() {
258
+ const container = document.getElementById('messagesContainer');
259
+ if (!container) return;
260
+
261
+ // Clear existing messages except system welcome
262
+ container.innerHTML = '';
263
+
264
+ // Add welcome message
265
+ const welcomeMessage = {
266
+ id: 0,
267
+ content: `Welcome to ChatVerse Retro v1.0\n> Type /help for available commands\n> Connection established: ONLINE`,
268
+ sender: 'SYSTEM',
269
+ timestamp: new Date(),
270
+ type: 'system'
271
+ };
272
+ this.displayMessage(welcomeMessage);
273
+
274
+ // Load and display stored messages
275
+ this.messages.forEach(message => {
276
+ this.displayMessage(message);
277
+ });
278
+ }
279
+
280
+ saveMessages() {
281
+ // Keep only last 50 messages
282
+ this.messages = this.messages.slice(-50);
283
+ localStorage.setItem('messages', JSON.stringify(this.messages));
284
+ }
285
+
286
+ updateContacts() {
287
+ // This would be called by the sidebar component
288
+ const event = new CustomEvent('contactsUpdated', { detail: this.contacts });
289
+ document.dispatchEvent(event);
290
+ }
291
+
292
+ formatTime(date) {
293
+ const now = new Date();
294
+ const diff = now - date;
295
+ const minutes = Math.floor(diff / 60000);
296
+
297
+ if (minutes < 1) return 'NOW';
298
+ if (minutes < 60) return `${minutes}M`;
299
+
300
+ const hours = Math.floor(minutes / 60);
301
+ if (hours < 24) return `${hours}H`;
302
+
303
+ return date.toLocaleDateString();
304
+ }
305
+
306
+ scrollToBottom() {
307
+ const container = document.getElementById('messagesContainer');
308
+ if (container) {
309
+ container.scrollTop = container.scrollHeight;
310
+ }
311
+ }
312
+
313
+ escapeHtml(text) {
314
+ const div = document.createElement('div');
315
+ div.textContent = text;
316
+ return div.innerHTML;
317
+ }
318
+ }
319
+
320
+ // Initialize app when DOM is loaded
321
+ document.addEventListener('DOMContentLoaded', () => {
322
+ window.chatApp = new ChatApp();
323
+ });
324
+
325
+ // Handle page visibility for retro effect
326
+ document.addEventListener('visibilitychange', () => {
327
+ if (document.hidden) {
328
+ document.body.style.filter = 'brightness(0.5)';
329
+ } else {
330
+ document.body.style.filter = 'brightness(1)';
331
+ }
332
+ });
333
+
334
+ // Add some retro console messages
335
+ console.log('%c ChatVerse Retro v1.0 Loaded ', 'background: #00ff00; color: #000; font-size: 20px; font-family: "Press Start 2P"');
336
+ console.log('%c Type chatApp.showHelp() in console for debug commands', 'color: #00ff00; font-family: monospace');
style.css CHANGED
@@ -1,28 +1,274 @@
1
- body {
2
- padding: 2rem;
3
- font-family: -apple-system, BlinkMacSystemFont, "Arial", sans-serif;
 
 
 
 
 
 
 
 
 
 
4
  }
5
 
6
- h1 {
7
- font-size: 16px;
8
- margin-top: 0;
 
 
 
 
 
 
 
 
 
9
  }
10
 
11
- p {
12
- color: rgb(107, 114, 128);
13
- font-size: 15px;
14
- margin-bottom: 10px;
15
- margin-top: 5px;
 
 
 
 
 
 
 
 
 
 
16
  }
17
 
18
- .card {
19
- max-width: 620px;
20
- margin: 0 auto;
21
- padding: 16px;
22
- border: 1px solid lightgray;
23
- border-radius: 16px;
 
 
 
 
 
 
 
 
 
 
 
 
24
  }
25
 
26
- .card p:last-child {
27
- margin-bottom: 0;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
28
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ /* Retro Terminal Theme */
2
+ :root {
3
+ --primary: #00ff00;
4
+ --primary-dark: #00cc00;
5
+ --secondary: #0a0a0a;
6
+ --accent: #00ff88;
7
+ --text-primary: #00ff00;
8
+ --text-secondary: #a0a0a0;
9
+ --text-muted: #505050;
10
+ --bg-dark: #000000;
11
+ --bg-darker: #050505;
12
+ --bg-light: #1a1a1a;
13
+ --border-color: #00ff00;
14
  }
15
 
16
+ [data-theme="light"] {
17
+ --primary: #00cc00;
18
+ --primary-dark: #009900;
19
+ --secondary: #f0f0f0;
20
+ --accent: #00cc66;
21
+ --text-primary: #00cc00;
22
+ --text-secondary: #404040;
23
+ --text-muted: #a0a0a0;
24
+ --bg-dark: #ffffff;
25
+ --bg-darker: #f5f5f5;
26
+ --bg-light: #e0e0e0;
27
+ --border-color: #00cc00;
28
  }
29
 
30
+ /* CRT Effect */
31
+ .retro-bg {
32
+ background:
33
+ radial-gradient(ellipse at center, var(--bg-darker) 0%, var(--bg-dark) 100%),
34
+ repeating-linear-gradient(
35
+ 0deg,
36
+ transparent,
37
+ transparent 2px,
38
+ rgba(0, 255, 0, 0.03) 2px,
39
+ rgba(0, 255, 0, 0.03) 4px
40
+ );
41
+ background-blend-mode: multiply;
42
+ position: relative;
43
+ color: var(--text-primary);
44
+ font-family: 'Courier New', monospace;
45
  }
46
 
47
+ .crt-overlay {
48
+ position: fixed;
49
+ top: 0;
50
+ left: 0;
51
+ width: 100%;
52
+ height: 100%;
53
+ background:
54
+ radial-gradient(ellipse at center, transparent 0%, rgba(0, 0, 0, 0.3) 100%),
55
+ repeating-linear-gradient(
56
+ 0deg,
57
+ rgba(0, 255, 0, 0.05) 0px,
58
+ transparent 1px,
59
+ transparent 2px,
60
+ rgba(0, 255, 0, 0.05) 3px
61
+ );
62
+ pointer-events: none;
63
+ z-index: 1000;
64
+ mix-blend-mode: screen;
65
  }
66
 
67
+ .scanlines {
68
+ position: fixed;
69
+ top: 0;
70
+ left: 0;
71
+ width: 100%;
72
+ height: 100%;
73
+ background: repeating-linear-gradient(
74
+ 0deg,
75
+ transparent,
76
+ transparent 1px,
77
+ rgba(0, 255, 0, 0.03) 1px,
78
+ rgba(0, 255, 0, 0.03) 2px
79
+ );
80
+ pointer-events: none;
81
+ z-index: 1001;
82
+ animation: scanlines 8s linear infinite;
83
  }
84
+
85
+ @keyframes scanlines {
86
+ 0% { transform: translateY(0); }
87
+ 100% { transform: translateY(10px); }
88
+ }
89
+
90
+ /* Glitch Effect */
91
+ .glitch {
92
+ position: relative;
93
+ color: var(--text-primary);
94
+ font-size: 1.5rem;
95
+ font-weight: bold;
96
+ text-transform: uppercase;
97
+ }
98
+
99
+ .glitch::before,
100
+ .glitch::after {
101
+ content: attr(data-text);
102
+ position: absolute;
103
+ top: 0;
104
+ left: 0;
105
+ width: 100%;
106
+ height: 100%;
107
+ }
108
+
109
+ .glitch::before {
110
+ animation: glitch-1 0.5s infinite;
111
+ color: #ff00ff;
112
+ z-index: -1;
113
+ }
114
+
115
+ .glitch::after {
116
+ animation: glitch-2 0.5s infinite;
117
+ color: #00ffff;
118
+ z-index: -2;
119
+ }
120
+
121
+ @keyframes glitch-1 {
122
+ 0%, 100% { transform: translate(0); }
123
+ 20% { transform: translate(-2px, 2px); }
124
+ 40% { transform: translate(-2px, -2px); }
125
+ 60% { transform: translate(2px, 2px); }
126
+ 80% { transform: translate(2px, -2px); }
127
+ }
128
+
129
+ @keyframes glitch-2 {
130
+ 0%, 100% { transform: translate(0); }
131
+ 20% { transform: translate(2px, 2px); }
132
+ 40% { transform: translate(2px, -2px); }
133
+ 60% { transform: translate(-2px, 2px); }
134
+ 80% { transform: translate(-2px, -2px); }
135
+ }
136
+
137
+ /* Custom Scrollbar */
138
+ ::-webkit-scrollbar {
139
+ width: 10px;
140
+ }
141
+
142
+ ::-webkit-scrollbar-track {
143
+ background: var(--bg-darker);
144
+ border: 1px solid var(--border-color);
145
+ }
146
+
147
+ ::-webkit-scrollbar-thumb {
148
+ background: var(--primary);
149
+ border: 1px solid var(--primary-dark);
150
+ }
151
+
152
+ ::-webkit-scrollbar-thumb:hover {
153
+ background: var(--primary-dark);
154
+ }
155
+
156
+ /* Message Animations */
157
+ .message-group {
158
+ opacity: 0;
159
+ transform: translateY(20px);
160
+ animation: messageAppear 0.3s ease-out forwards;
161
+ }
162
+
163
+ @keyframes messageAppear {
164
+ to {
165
+ opacity: 1;
166
+ transform: translateY(0);
167
+ }
168
+ }
169
+
170
+ /* Button Press Effects */
171
+ .retro-button {
172
+ position: relative;
173
+ background: var(--primary);
174
+ color: var(--bg-dark);
175
+ border: none;
176
+ padding: 0.75rem 1.5rem;
177
+ font-family: 'Press Start 2P', cursive;
178
+ font-size: 0.75rem;
179
+ cursor: pointer;
180
+ transition: all 0.1s;
181
+ box-shadow: 0 4px 0 var(--primary-dark);
182
+ }
183
+
184
+ .retro-button:hover {
185
+ transform: translateY(-2px);
186
+ box-shadow: 0 6px 0 var(--primary-dark);
187
+ }
188
+
189
+ .retro-button:active {
190
+ transform: translateY(4px);
191
+ box-shadow: 0 0 0 var(--primary-dark);
192
+ }
193
+
194
+ /* Input Focus Effects */
195
+ .retro-input:focus {
196
+ animation: pulse 1.5s infinite;
197
+ }
198
+
199
+ @keyframes pulse {
200
+ 0%, 100% { box-shadow: 0 0 15px var(--primary); }
201
+ 50% { box-shadow: 0 0 25px var(--primary); }
202
+ }
203
+
204
+ /* Theme Transition */
205
+ * {
206
+ transition: background-color 0.3s, color 0.3s, border-color 0.3s;
207
+ }
208
+
209
+ /* Mobile Responsive */
210
+ @media (max-width: 768px) {
211
+ .sidebar-collapsed {
212
+ transform: translateX(-100%);
213
+ }
214
+
215
+ .sidebar-expanded {
216
+ transform: translateX(0);
217
+ }
218
+
219
+ .mobile-hidden {
220
+ display: none;
221
+ }
222
+
223
+ .mobile-full {
224
+ width: 100%;
225
+ }
226
+ }
227
+
228
+ /* Loading Animation */
229
+ .loading-dots {
230
+ display: inline-block;
231
+ }
232
+
233
+ .loading-dots::after {
234
+ content: '';
235
+ animation: dots 1.5s infinite;
236
+ }
237
+
238
+ @keyframes dots {
239
+ 0%, 20% { content: '.'; }
240
+ 40% { content: '..'; }
241
+ 60%, 100% { content: '...'; }
242
+ }
243
+
244
+ /* Status Indicators */
245
+ .status-online {
246
+ background: var(--primary);
247
+ box-shadow: 0 0 10px var(--primary);
248
+ }
249
+
250
+ .status-away {
251
+ background: #ffaa00;
252
+ box-shadow: 0 0 10px #ffaa00;
253
+ }
254
+
255
+ .status-offline {
256
+ background: var(--text-muted);
257
+ }
258
+
259
+ /* Hover Effects */
260
+ .hover-glow:hover {
261
+ filter: brightness(1.2);
262
+ text-shadow: 0 0 10px var(--primary);
263
+ }
264
+
265
+ /* Terminal Cursor */
266
+ .terminal-cursor::after {
267
+ content: '_';
268
+ animation: blink 1s infinite;
269
+ }
270
+
271
+ @keyframes blink {
272
+ 0%, 50% { opacity: 1; }
273
+ 51%, 100% { opacity: 0; }
274
+ }