ulduldp commited on
Commit
7b2ef27
·
verified ·
1 Parent(s): 9fcb962

Update public/index.html

Browse files
Files changed (1) hide show
  1. public/index.html +104 -158
public/index.html CHANGED
@@ -5,103 +5,71 @@
5
  <title>Live Logs</title>
6
 
7
  <style>
8
- body {
9
- margin: 0;
10
- background: #0b1020;
11
- color: #e2e8f0;
12
- font-family: monospace;
13
- display: flex;
14
- flex-direction: column;
15
- height: 90vh;
16
- }
17
-
18
- header {
19
- padding: 10px;
20
- background: #111827;
21
- border-bottom: 1px solid #1f2937;
22
- }
23
-
24
- #logs {
25
- flex: 1;
26
- overflow-y: auto;
27
- padding: 10px;
28
- font-size: 13px;
29
- }
30
-
31
- .log {
32
- margin-bottom: 6px;
33
- word-break: break-word;
34
- }
35
 
36
- .time {
37
- color: #64748b;
38
- margin-right: 6px;
39
- }
 
40
 
41
- footer {
42
- position: relative;
43
- display: flex;
44
- padding: 8px;
45
- background: #111827;
46
- border-top: 1px solid #1f2937;
47
- }
48
 
49
- input {
50
- flex: 1;
51
- padding: 10px;
52
- background: #0b1020;
53
- border: none;
54
- color: white;
55
- border-radius: 6px;
56
- outline: none;
57
- }
58
 
59
- button {
60
- margin-left: 8px;
61
- padding: 10px 14px;
62
- background: #2563eb;
63
- border: none;
64
- color: white;
65
- border-radius: 6px;
66
- }
67
 
68
- #suggestions {
69
- position: absolute;
70
- bottom: 60px;
71
- left: 10px;
72
- right: 10px;
73
- background: #111827;
74
- border: 1px solid #1f2937;
75
- border-radius: 6px;
76
- max-height: 150px;
77
- overflow-y: auto;
78
- display: none;
79
- z-index: 10;
80
- }
81
 
82
- .suggestion-item {
83
- padding: 8px;
84
- cursor: pointer;
85
- }
 
 
 
86
 
87
- .suggestion-item:hover {
88
- background: #1f2937;
89
- }
 
 
 
 
90
  </style>
91
  </head>
92
 
93
  <body>
94
 
95
  <header>📡 Live Logs Terminal</header>
96
-
97
  <div id="logs"></div>
98
 
99
  <footer>
100
- <input id="cmd" placeholder="Enter command..." autocomplete="off" />
101
  <button onclick="sendCmd()">Send</button>
102
-
103
- <!-- Suggestions -->
104
- <div id="suggestions"></div>
105
  </footer>
106
 
107
  <script src="/socket.io/socket.io.js"></script>
@@ -109,8 +77,16 @@
109
  const socket = io();
110
  const logsDiv = document.getElementById('logs');
111
  const input = document.getElementById('cmd');
112
- const suggestionBox = document.getElementById('suggestions');
113
 
 
 
 
 
 
 
 
 
 
114
  function formatTime(ts) {
115
  return new Date(ts).toLocaleString('en-IN', {
116
  timeZone: 'Asia/Kolkata',
@@ -118,119 +94,89 @@ function formatTime(ts) {
118
  });
119
  }
120
 
121
- /*function addLog(log) {
122
- const div = document.createElement('div');
123
- div.className = 'log';
124
-
125
- div.innerHTML = `
126
- <span class="time">[${formatTime(log.timestamp)}]</span>
127
- ${log.html}
128
- `;
129
-
130
- logsDiv.appendChild(div);
131
- logsDiv.scrollTop = logsDiv.scrollHeight;
132
- }*/
133
-
134
- function addLog(log) {
135
  const div = document.createElement('div');
136
  div.className = 'log';
137
-
138
  div.innerHTML = `
139
  <span class="time">[${formatTime(log.timestamp)}]</span>
140
  ${log.html}
141
  `;
 
 
142
 
143
- // check if user already near bottom
144
- const isNearBottom =
145
- logsDiv.scrollHeight - logsDiv.scrollTop <= logsDiv.clientHeight + 50;
 
146
 
147
- logsDiv.appendChild(div);
 
148
 
149
- // only scroll if user was at bottom
150
- if (isNearBottom) {
151
- logsDiv.scrollTop = logsDiv.scrollHeight;
152
- }
153
- }
154
 
155
- // ================= HISTORY =================
156
- function getHistory() {
157
- return JSON.parse(localStorage.getItem('cmd_history') || '[]');
158
- }
159
 
160
- function saveToHistory(cmd) {
161
- let history = getHistory();
 
162
 
163
- history = history.filter(c => c !== cmd);
164
- history.unshift(cmd);
165
 
166
- if (history.length > 50) history.pop();
167
 
168
- localStorage.setItem('cmd_history', JSON.stringify(history));
169
  }
170
 
171
- // ================= SUGGESTIONS =================
172
- function showSuggestions(value) {
173
- const history = getHistory();
174
-
175
- const filtered = history.filter(cmd =>
176
- cmd.toLowerCase().includes(value.toLowerCase())
177
- );
178
-
179
- if (!value || filtered.length === 0) {
180
- suggestionBox.style.display = 'none';
181
- return;
182
- }
183
-
184
- suggestionBox.innerHTML = '';
185
 
186
- filtered.slice(0, 5).forEach(cmd => {
187
- const div = document.createElement('div');
188
- div.className = 'suggestion-item';
189
- div.textContent = cmd;
190
 
191
- div.onclick = () => {
192
- input.value = cmd;
193
- suggestionBox.style.display = 'none';
194
- };
195
 
196
- suggestionBox.appendChild(div);
197
- });
 
 
198
 
199
- suggestionBox.style.display = 'block';
 
 
200
  }
201
 
202
- // ================= EVENTS =================
203
- input.addEventListener('input', () => {
204
- showSuggestions(input.value);
205
- });
206
 
207
- input.addEventListener('keypress', e => {
208
- if (e.key === 'Enter') sendCmd();
 
 
 
209
  });
210
 
211
- // ================= COMMAND SEND =================
212
  function sendCmd() {
213
  const val = input.value.trim();
214
  if (!val) return;
215
 
216
-
217
  socket.emit('command', { command: val });
218
 
219
- saveToHistory(val);
 
 
 
220
 
221
  input.value = '';
222
- suggestionBox.style.display = 'none';
223
- if(val === "clear") { return logsDiv.innerHTML = "";};
224
  }
225
 
226
- // ================= LOAD OLD LOGS =================
227
- fetch('/api/logs')
228
- .then(res => res.json())
229
- .then(data => data.forEach(addLog));
230
-
231
- // ================= LIVE LOGS =================
232
- socket.on('log', addLog);
233
 
 
 
234
  </script>
235
 
236
  </body>
 
5
  <title>Live Logs</title>
6
 
7
  <style>
8
+ body {
9
+ margin: 0;
10
+ background: #0b1020;
11
+ color: #e2e8f0;
12
+ font-family: monospace;
13
+ display: flex;
14
+ flex-direction: column;
15
+ height: 90vh;
16
+ }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
17
 
18
+ header {
19
+ padding: 10px;
20
+ background: #111827;
21
+ border-bottom: 1px solid #1f2937;
22
+ }
23
 
24
+ #logs {
25
+ flex: 1;
26
+ overflow-y: auto;
27
+ padding: 10px;
28
+ font-size: 13px;
29
+ }
 
30
 
31
+ .log {
32
+ margin-bottom: 6px;
33
+ word-break: break-word;
34
+ }
 
 
 
 
 
35
 
36
+ .time {
37
+ color: #64748b;
38
+ margin-right: 6px;
39
+ }
 
 
 
 
40
 
41
+ footer {
42
+ display: flex;
43
+ padding: 8px;
44
+ background: #111827;
45
+ }
 
 
 
 
 
 
 
 
46
 
47
+ input {
48
+ flex: 1;
49
+ padding: 10px;
50
+ background: #0b1020;
51
+ border: none;
52
+ color: white;
53
+ }
54
 
55
+ button {
56
+ margin-left: 8px;
57
+ padding: 10px;
58
+ background: #2563eb;
59
+ border: none;
60
+ color: white;
61
+ }
62
  </style>
63
  </head>
64
 
65
  <body>
66
 
67
  <header>📡 Live Logs Terminal</header>
 
68
  <div id="logs"></div>
69
 
70
  <footer>
71
+ <input id="cmd" placeholder="Enter command..." />
72
  <button onclick="sendCmd()">Send</button>
 
 
 
73
  </footer>
74
 
75
  <script src="/socket.io/socket.io.js"></script>
 
77
  const socket = io();
78
  const logsDiv = document.getElementById('logs');
79
  const input = document.getElementById('cmd');
 
80
 
81
+ // ===== CONFIG =====
82
+ const LIMIT = 50;
83
+ const MAX_DOM_LOGS = 200;
84
+
85
+ let offset = 0;
86
+ let loading = false;
87
+ let hasMore = true;
88
+
89
+ // ===== HELPERS =====
90
  function formatTime(ts) {
91
  return new Date(ts).toLocaleString('en-IN', {
92
  timeZone: 'Asia/Kolkata',
 
94
  });
95
  }
96
 
97
+ function createLogElement(log) {
 
 
 
 
 
 
 
 
 
 
 
 
 
98
  const div = document.createElement('div');
99
  div.className = 'log';
 
100
  div.innerHTML = `
101
  <span class="time">[${formatTime(log.timestamp)}]</span>
102
  ${log.html}
103
  `;
104
+ return div;
105
+ }
106
 
107
+ // ===== LOAD OLD LOGS =====
108
+ async function loadLogs() {
109
+ if (loading || !hasMore) return;
110
+ loading = true;
111
 
112
+ const res = await fetch(`/api/logs?limit=${LIMIT}&offset=${offset}`);
113
+ const data = await res.json();
114
 
115
+ hasMore = data.hasMore;
 
 
 
 
116
 
117
+ const prevHeight = logsDiv.scrollHeight;
 
 
 
118
 
119
+ data.logs.reverse().forEach(log => {
120
+ logsDiv.prepend(createLogElement(log));
121
+ });
122
 
123
+ offset += data.logs.length;
 
124
 
125
+ logsDiv.scrollTop = logsDiv.scrollHeight - prevHeight;
126
 
127
+ loading = false;
128
  }
129
 
130
+ // ===== LIVE LOGS =====
131
+ function addLog(log) {
132
+ const div = createLogElement(log);
 
 
 
 
 
 
 
 
 
 
 
133
 
134
+ const isNearBottom =
135
+ logsDiv.scrollHeight - logsDiv.scrollTop <= logsDiv.clientHeight + 50;
 
 
136
 
137
+ logsDiv.appendChild(div);
 
 
 
138
 
139
+ // cleanup old DOM
140
+ if (logsDiv.children.length > MAX_DOM_LOGS) {
141
+ logsDiv.removeChild(logsDiv.firstChild);
142
+ }
143
 
144
+ if (isNearBottom) {
145
+ logsDiv.scrollTop = logsDiv.scrollHeight;
146
+ }
147
  }
148
 
149
+ socket.on('log', addLog);
 
 
 
150
 
151
+ // ===== SCROLL LOAD =====
152
+ logsDiv.addEventListener('scroll', () => {
153
+ if (logsDiv.scrollTop < 50) {
154
+ loadLogs();
155
+ }
156
  });
157
 
158
+ // ===== SEND CMD =====
159
  function sendCmd() {
160
  const val = input.value.trim();
161
  if (!val) return;
162
 
 
163
  socket.emit('command', { command: val });
164
 
165
+ if (val === "clear") {
166
+ logsDiv.innerHTML = "";
167
+ return;
168
+ }
169
 
170
  input.value = '';
 
 
171
  }
172
 
173
+ // enter key
174
+ input.addEventListener('keypress', e => {
175
+ if (e.key === 'Enter') sendCmd();
176
+ });
 
 
 
177
 
178
+ // ===== INIT =====
179
+ loadLogs();
180
  </script>
181
 
182
  </body>