@neuraiproject/neurai-depin-terminal 1.0.0 → 2.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +37 -8
- package/dist/index.cjs +143 -184
- package/package.json +12 -12
- package/src/config/ConfigManager.js +43 -26
- package/src/constants.js +18 -5
- package/src/domain/messageTypes.js +26 -0
- package/src/errors.js +17 -0
- package/src/index.js +129 -30
- package/src/messaging/MessagePoller.js +132 -3
- package/src/messaging/MessageSender.js +99 -41
- package/src/messaging/MessageStore.js +26 -0
- package/src/messaging/RecipientDirectory.js +186 -0
- package/src/ui/CharsmUI.js +690 -0
- package/src/ui/RecipientSelector.js +114 -0
- package/src/ui/TabManager.js +162 -0
- package/src/ui/render.js +173 -0
- package/src/utils.js +304 -43
- package/src/wallet/WalletManager.js +5 -6
- package/src/ui/TerminalUI.js +0 -272
- package/src/ui/components/ErrorOverlay.js +0 -99
- package/src/ui/components/InputBox.js +0 -63
- package/src/ui/components/MessageBox.js +0 -51
- package/src/ui/components/StatusBar.js +0 -32
- package/src/ui/components/TopBar.js +0 -63
package/src/ui/TerminalUI.js
DELETED
|
@@ -1,272 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Terminal UI for Neurai DePIN Terminal
|
|
3
|
-
* Full-screen blessed-based interface with top bar, message area, input, and status bar
|
|
4
|
-
* @module TerminalUI
|
|
5
|
-
*/
|
|
6
|
-
|
|
7
|
-
import blessed from 'blessed';
|
|
8
|
-
import {
|
|
9
|
-
COLORS,
|
|
10
|
-
ICONS,
|
|
11
|
-
BLESSED_KEYS,
|
|
12
|
-
ADDRESS,
|
|
13
|
-
PRIVACY,
|
|
14
|
-
TIME
|
|
15
|
-
} from '../constants.js';
|
|
16
|
-
import { formatTimestamp, resetTerminal } from '../utils.js';
|
|
17
|
-
import { TopBar } from './components/TopBar.js';
|
|
18
|
-
import { MessageBox } from './components/MessageBox.js';
|
|
19
|
-
import { InputBox } from './components/InputBox.js';
|
|
20
|
-
import { StatusBar } from './components/StatusBar.js';
|
|
21
|
-
import { ErrorOverlay } from './components/ErrorOverlay.js';
|
|
22
|
-
|
|
23
|
-
/**
|
|
24
|
-
* Terminal UI manager using blessed library
|
|
25
|
-
*/
|
|
26
|
-
export class TerminalUI {
|
|
27
|
-
/**
|
|
28
|
-
* Create a new TerminalUI instance
|
|
29
|
-
* @param {Object} config - Configuration object
|
|
30
|
-
* @param {string} config.rpc_url - RPC server URL
|
|
31
|
-
* @param {string} config.token - DePIN token name
|
|
32
|
-
* @param {WalletManager} walletManager - Wallet manager instance
|
|
33
|
-
* @param {RpcService} rpcService - RPC service instance
|
|
34
|
-
*/
|
|
35
|
-
constructor(config, walletManager, rpcService) {
|
|
36
|
-
this.config = config;
|
|
37
|
-
this.walletManager = walletManager;
|
|
38
|
-
this.rpcService = rpcService;
|
|
39
|
-
this.myAddress = walletManager.getAddress();
|
|
40
|
-
this.sendCallback = null;
|
|
41
|
-
this.displayedMessages = [];
|
|
42
|
-
this.hasPrivacy = false;
|
|
43
|
-
this.encryptionType = PRIVACY.DEFAULT_ENCRYPTION;
|
|
44
|
-
this.totalMessages = 0;
|
|
45
|
-
this.lastConnectionStatus = false;
|
|
46
|
-
this.lastPollTime = null;
|
|
47
|
-
|
|
48
|
-
this.initializeScreen();
|
|
49
|
-
this.createComponents();
|
|
50
|
-
this.setupKeybindings();
|
|
51
|
-
this.inputBox.focus();
|
|
52
|
-
this.screen.render();
|
|
53
|
-
this.updateTopBar({ connected: false, lastPoll: null });
|
|
54
|
-
}
|
|
55
|
-
|
|
56
|
-
/**
|
|
57
|
-
* Initialize blessed screen
|
|
58
|
-
*/
|
|
59
|
-
initializeScreen() {
|
|
60
|
-
this.screen = blessed.screen({
|
|
61
|
-
smartCSR: true,
|
|
62
|
-
title: 'Neurai DePIN Terminal'
|
|
63
|
-
});
|
|
64
|
-
}
|
|
65
|
-
|
|
66
|
-
/**
|
|
67
|
-
* Create UI components (top bar, message box, input box, status bar)
|
|
68
|
-
*/
|
|
69
|
-
createComponents() {
|
|
70
|
-
this.topBar = new TopBar(this.screen, this.config, this.myAddress);
|
|
71
|
-
this.messageBox = new MessageBox(this.screen);
|
|
72
|
-
this.inputBox = new InputBox(this.screen, (msg) => this.handleSend(msg));
|
|
73
|
-
this.statusBar = new StatusBar(this.screen);
|
|
74
|
-
this.errorOverlay = new ErrorOverlay(this.screen);
|
|
75
|
-
}
|
|
76
|
-
|
|
77
|
-
/**
|
|
78
|
-
* Show blocking error overlay
|
|
79
|
-
* @param {string[]} errors - List of error messages
|
|
80
|
-
*/
|
|
81
|
-
showBlockingErrors(errors) {
|
|
82
|
-
this.inputBox.disable();
|
|
83
|
-
this.errorOverlay.show(errors);
|
|
84
|
-
}
|
|
85
|
-
|
|
86
|
-
/**
|
|
87
|
-
* Clear blocking error overlay
|
|
88
|
-
*/
|
|
89
|
-
clearBlockingErrors() {
|
|
90
|
-
if (this.errorOverlay.isVisible()) {
|
|
91
|
-
this.errorOverlay.hide();
|
|
92
|
-
this.inputBox.enable();
|
|
93
|
-
this.inputBox.focus();
|
|
94
|
-
}
|
|
95
|
-
}
|
|
96
|
-
|
|
97
|
-
/**
|
|
98
|
-
* Setup keyboard bindings
|
|
99
|
-
*/
|
|
100
|
-
setupKeybindings() {
|
|
101
|
-
// Exit on Ctrl+C or Escape
|
|
102
|
-
this.screen.key(BLESSED_KEYS.QUIT, () => {
|
|
103
|
-
this.cleanup();
|
|
104
|
-
process.exit(0);
|
|
105
|
-
});
|
|
106
|
-
}
|
|
107
|
-
|
|
108
|
-
/**
|
|
109
|
-
* Update top bar with connection status and info
|
|
110
|
-
* @param {Object} status - Status object
|
|
111
|
-
* @param {boolean} status.connected - Connection status
|
|
112
|
-
* @param {Date|null} status.lastPoll - Last poll time
|
|
113
|
-
*/
|
|
114
|
-
updateTopBar(status) {
|
|
115
|
-
this.lastConnectionStatus = status.connected;
|
|
116
|
-
|
|
117
|
-
if (status.lastPoll) {
|
|
118
|
-
this.lastPollTime = status.lastPoll;
|
|
119
|
-
}
|
|
120
|
-
|
|
121
|
-
this.topBar.update({
|
|
122
|
-
connected: status.connected,
|
|
123
|
-
lastPoll: status.lastPoll
|
|
124
|
-
});
|
|
125
|
-
}
|
|
126
|
-
|
|
127
|
-
/**
|
|
128
|
-
* Format message line for display
|
|
129
|
-
* @param {Object} msg - Message object
|
|
130
|
-
* @returns {string} Formatted message line with blessed tags
|
|
131
|
-
*/
|
|
132
|
-
formatMessageLine(msg) {
|
|
133
|
-
const time = formatTimestamp(msg.timestamp, this.config.timezone);
|
|
134
|
-
const isMe = msg.sender === this.myAddress;
|
|
135
|
-
const senderLabel = isMe ? 'YOU' : msg.sender.slice(0, ADDRESS.TRUNCATE_LENGTH);
|
|
136
|
-
const color = isMe ? COLORS.MY_MESSAGE : COLORS.OTHER_MESSAGE;
|
|
137
|
-
|
|
138
|
-
return `{${color}}[${time}] ${senderLabel}: ${msg.message}{/}`;
|
|
139
|
-
}
|
|
140
|
-
|
|
141
|
-
/**
|
|
142
|
-
* Add a new message to the display
|
|
143
|
-
* Maintains chronological order (oldest to newest)
|
|
144
|
-
* @param {Object} msg - Message object
|
|
145
|
-
* @param {string} msg.sender - Sender address
|
|
146
|
-
* @param {string} msg.message - Message content
|
|
147
|
-
* @param {number} msg.timestamp - Unix timestamp in seconds
|
|
148
|
-
* @param {string} msg.hash - Message hash
|
|
149
|
-
*/
|
|
150
|
-
addMessage(msg) {
|
|
151
|
-
this.displayedMessages.push(msg);
|
|
152
|
-
|
|
153
|
-
// Sort by timestamp (oldest to newest)
|
|
154
|
-
this.displayedMessages.sort((a, b) => a.timestamp - b.timestamp);
|
|
155
|
-
|
|
156
|
-
// Redraw all messages
|
|
157
|
-
this.redrawMessages();
|
|
158
|
-
}
|
|
159
|
-
|
|
160
|
-
/**
|
|
161
|
-
* Redraw all messages in the message box
|
|
162
|
-
*/
|
|
163
|
-
redrawMessages() {
|
|
164
|
-
const formattedMessages = this.displayedMessages.map(msg => this.formatMessageLine(msg));
|
|
165
|
-
// Clear and rebuild message box content
|
|
166
|
-
this.messageBox.component.setContent(formattedMessages.join('\n'));
|
|
167
|
-
this.messageBox.component.setScrollPerc(100);
|
|
168
|
-
this.screen.render();
|
|
169
|
-
}
|
|
170
|
-
|
|
171
|
-
/**
|
|
172
|
-
* Show error message in message box
|
|
173
|
-
* @param {string} errorMsg - Error message
|
|
174
|
-
*/
|
|
175
|
-
showError(errorMsg) {
|
|
176
|
-
const line = `{${COLORS.ERROR}}[ERROR] ${errorMsg}{/}`;
|
|
177
|
-
this.messageBox.addMessage(line);
|
|
178
|
-
}
|
|
179
|
-
|
|
180
|
-
/**
|
|
181
|
-
* Show info message in message box
|
|
182
|
-
* @param {string} infoMsg - Info message
|
|
183
|
-
*/
|
|
184
|
-
showInfo(infoMsg) {
|
|
185
|
-
const line = `{${COLORS.INFO}}[INFO] ${infoMsg}{/}`;
|
|
186
|
-
this.messageBox.addMessage(line);
|
|
187
|
-
}
|
|
188
|
-
|
|
189
|
-
/**
|
|
190
|
-
* Show success message in message box
|
|
191
|
-
* @param {string} successMsg - Success message
|
|
192
|
-
*/
|
|
193
|
-
showSuccess(successMsg) {
|
|
194
|
-
const line = `{${COLORS.SUCCESS}}[${ICONS.SUCCESS}] ${successMsg}{/}`;
|
|
195
|
-
this.messageBox.addMessage(line);
|
|
196
|
-
}
|
|
197
|
-
|
|
198
|
-
/**
|
|
199
|
-
* Update status bar with send status
|
|
200
|
-
* @param {string} message - Status message
|
|
201
|
-
* @param {string} [type='info'] - Message type: 'success', 'error', or 'info'
|
|
202
|
-
*/
|
|
203
|
-
updateSendStatus(message, type = 'info') {
|
|
204
|
-
this.statusBar.update(message, type);
|
|
205
|
-
}
|
|
206
|
-
|
|
207
|
-
/**
|
|
208
|
-
* Clear status bar
|
|
209
|
-
*/
|
|
210
|
-
clearSendStatus() {
|
|
211
|
-
this.statusBar.update('');
|
|
212
|
-
}
|
|
213
|
-
|
|
214
|
-
/**
|
|
215
|
-
* Handle send message action
|
|
216
|
-
* @param {string} message - Message to send
|
|
217
|
-
*/
|
|
218
|
-
handleSend(message) {
|
|
219
|
-
if (!message) return;
|
|
220
|
-
|
|
221
|
-
if (this.sendCallback) {
|
|
222
|
-
this.sendCallback(message);
|
|
223
|
-
}
|
|
224
|
-
}
|
|
225
|
-
|
|
226
|
-
/**
|
|
227
|
-
* Register callback for send action
|
|
228
|
-
* @param {Function} callback - Callback function(message)
|
|
229
|
-
*/
|
|
230
|
-
onSend(callback) {
|
|
231
|
-
this.sendCallback = callback;
|
|
232
|
-
}
|
|
233
|
-
|
|
234
|
-
/**
|
|
235
|
-
* Update pool information from depingetmsginfo RPC call
|
|
236
|
-
* @param {Object} poolInfo - Pool information
|
|
237
|
-
* @param {number} [poolInfo.messages] - Total messages in pool
|
|
238
|
-
* @param {string} [poolInfo.cipher] - Encryption cipher name
|
|
239
|
-
* @param {string} [poolInfo.depinpoolpkey] - Server privacy public key
|
|
240
|
-
*/
|
|
241
|
-
updatePoolInfo(poolInfo) {
|
|
242
|
-
if (poolInfo) {
|
|
243
|
-
this.totalMessages = poolInfo.messages || 0;
|
|
244
|
-
this.encryptionType = poolInfo.cipher || PRIVACY.DEFAULT_ENCRYPTION;
|
|
245
|
-
this.hasPrivacy = poolInfo.depinpoolpkey && poolInfo.depinpoolpkey !== PRIVACY.NO_KEY_VALUE;
|
|
246
|
-
|
|
247
|
-
this.topBar.setTotalMessages(this.totalMessages);
|
|
248
|
-
this.topBar.setEncryptionType(this.encryptionType);
|
|
249
|
-
|
|
250
|
-
// Refresh top bar
|
|
251
|
-
this.updateTopBar({
|
|
252
|
-
connected: this.lastConnectionStatus,
|
|
253
|
-
lastPoll: this.lastPollTime
|
|
254
|
-
});
|
|
255
|
-
}
|
|
256
|
-
}
|
|
257
|
-
|
|
258
|
-
/**
|
|
259
|
-
* Cleanup terminal state
|
|
260
|
-
* Removes listeners, destroys screen, resets terminal
|
|
261
|
-
*/
|
|
262
|
-
cleanup() {
|
|
263
|
-
if (this.screen) {
|
|
264
|
-
try {
|
|
265
|
-
this.screen.destroy();
|
|
266
|
-
} catch (err) {
|
|
267
|
-
// Ignore cleanup errors
|
|
268
|
-
}
|
|
269
|
-
resetTerminal();
|
|
270
|
-
}
|
|
271
|
-
}
|
|
272
|
-
}
|
|
@@ -1,99 +0,0 @@
|
|
|
1
|
-
import blessed from 'blessed';
|
|
2
|
-
import { COLORS } from '../../constants.js';
|
|
3
|
-
|
|
4
|
-
export class ErrorOverlay {
|
|
5
|
-
constructor(screen) {
|
|
6
|
-
this.screen = screen;
|
|
7
|
-
this.timer = null;
|
|
8
|
-
this.timeLeft = 30;
|
|
9
|
-
this.currentErrors = [];
|
|
10
|
-
|
|
11
|
-
this.component = blessed.box({
|
|
12
|
-
top: 'center',
|
|
13
|
-
left: 'center',
|
|
14
|
-
width: '50%',
|
|
15
|
-
height: 'shrink',
|
|
16
|
-
content: '',
|
|
17
|
-
tags: true,
|
|
18
|
-
border: {
|
|
19
|
-
type: 'line'
|
|
20
|
-
},
|
|
21
|
-
style: {
|
|
22
|
-
fg: COLORS.FG_WHITE,
|
|
23
|
-
bg: COLORS.ERROR,
|
|
24
|
-
border: {
|
|
25
|
-
fg: COLORS.FG_WHITE
|
|
26
|
-
}
|
|
27
|
-
},
|
|
28
|
-
hidden: true
|
|
29
|
-
});
|
|
30
|
-
|
|
31
|
-
this.screen.append(this.component);
|
|
32
|
-
}
|
|
33
|
-
|
|
34
|
-
/**
|
|
35
|
-
* Show blocking error overlay
|
|
36
|
-
* @param {string[]} errors - List of error messages
|
|
37
|
-
*/
|
|
38
|
-
show(errors) {
|
|
39
|
-
if (!errors || errors.length === 0) {
|
|
40
|
-
this.hide();
|
|
41
|
-
return;
|
|
42
|
-
}
|
|
43
|
-
|
|
44
|
-
this.currentErrors = errors;
|
|
45
|
-
this.timeLeft = 30;
|
|
46
|
-
|
|
47
|
-
// Clear existing timer if any
|
|
48
|
-
if (this.timer) {
|
|
49
|
-
clearInterval(this.timer);
|
|
50
|
-
}
|
|
51
|
-
|
|
52
|
-
this.updateContent();
|
|
53
|
-
this.component.show();
|
|
54
|
-
this.component.setFront();
|
|
55
|
-
this.screen.render();
|
|
56
|
-
|
|
57
|
-
// Start countdown
|
|
58
|
-
this.timer = setInterval(() => {
|
|
59
|
-
this.timeLeft--;
|
|
60
|
-
if (this.timeLeft < 0) this.timeLeft = 0;
|
|
61
|
-
this.updateContent();
|
|
62
|
-
}, 1000);
|
|
63
|
-
}
|
|
64
|
-
|
|
65
|
-
/**
|
|
66
|
-
* Update overlay content with current timer
|
|
67
|
-
*/
|
|
68
|
-
updateContent() {
|
|
69
|
-
const content = `\n{bold}CRITICAL ERRORS:{/bold}\n\n` +
|
|
70
|
-
this.currentErrors.map(e => `• ${e}`).join('\n\n') +
|
|
71
|
-
`\n\n{center}Retrying in ${this.timeLeft}s...{/center}\n`;
|
|
72
|
-
|
|
73
|
-
this.component.setContent(content);
|
|
74
|
-
this.screen.render();
|
|
75
|
-
}
|
|
76
|
-
|
|
77
|
-
/**
|
|
78
|
-
* Hide blocking error overlay
|
|
79
|
-
*/
|
|
80
|
-
hide() {
|
|
81
|
-
if (this.timer) {
|
|
82
|
-
clearInterval(this.timer);
|
|
83
|
-
this.timer = null;
|
|
84
|
-
}
|
|
85
|
-
|
|
86
|
-
if (!this.component.hidden) {
|
|
87
|
-
this.component.hide();
|
|
88
|
-
this.screen.render();
|
|
89
|
-
}
|
|
90
|
-
}
|
|
91
|
-
|
|
92
|
-
/**
|
|
93
|
-
* Check if overlay is visible
|
|
94
|
-
* @returns {boolean} True if visible
|
|
95
|
-
*/
|
|
96
|
-
isVisible() {
|
|
97
|
-
return !this.component.hidden;
|
|
98
|
-
}
|
|
99
|
-
}
|
|
@@ -1,63 +0,0 @@
|
|
|
1
|
-
import blessed from 'blessed';
|
|
2
|
-
import { UI, COLORS, KEY_CODES } from '../../constants.js';
|
|
3
|
-
|
|
4
|
-
export class InputBox {
|
|
5
|
-
constructor(screen, onSend) {
|
|
6
|
-
this.screen = screen;
|
|
7
|
-
this.onSend = onSend;
|
|
8
|
-
|
|
9
|
-
this.component = blessed.textarea({
|
|
10
|
-
bottom: UI.STATUS_BAR_HEIGHT,
|
|
11
|
-
left: 0,
|
|
12
|
-
width: '100%',
|
|
13
|
-
height: UI.INPUT_BOX_HEIGHT,
|
|
14
|
-
inputOnFocus: true,
|
|
15
|
-
keys: true,
|
|
16
|
-
style: {
|
|
17
|
-
fg: COLORS.FG_WHITE,
|
|
18
|
-
bg: COLORS.BG_BLACK,
|
|
19
|
-
border: {
|
|
20
|
-
fg: COLORS.BORDER
|
|
21
|
-
}
|
|
22
|
-
},
|
|
23
|
-
border: {
|
|
24
|
-
type: 'line'
|
|
25
|
-
}
|
|
26
|
-
});
|
|
27
|
-
|
|
28
|
-
this.setupEvents();
|
|
29
|
-
this.screen.append(this.component);
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
setupEvents() {
|
|
33
|
-
this.component.key('enter', () => {
|
|
34
|
-
const message = this.component.getValue().trim();
|
|
35
|
-
if (message) {
|
|
36
|
-
this.onSend(message);
|
|
37
|
-
this.component.clearValue();
|
|
38
|
-
this.screen.render();
|
|
39
|
-
}
|
|
40
|
-
});
|
|
41
|
-
}
|
|
42
|
-
|
|
43
|
-
focus() {
|
|
44
|
-
if (!this.disabled) {
|
|
45
|
-
this.component.focus();
|
|
46
|
-
}
|
|
47
|
-
}
|
|
48
|
-
|
|
49
|
-
disable() {
|
|
50
|
-
this.disabled = true;
|
|
51
|
-
this.component.inputOnFocus = false;
|
|
52
|
-
// Optional: Change style to indicate disabled state
|
|
53
|
-
this.component.style.border.fg = 'gray';
|
|
54
|
-
this.screen.render();
|
|
55
|
-
}
|
|
56
|
-
|
|
57
|
-
enable() {
|
|
58
|
-
this.disabled = false;
|
|
59
|
-
this.component.inputOnFocus = true;
|
|
60
|
-
this.component.style.border.fg = COLORS.BORDER;
|
|
61
|
-
this.screen.render();
|
|
62
|
-
}
|
|
63
|
-
}
|
|
@@ -1,51 +0,0 @@
|
|
|
1
|
-
import blessed from 'blessed';
|
|
2
|
-
import { UI, COLORS } from '../../constants.js';
|
|
3
|
-
|
|
4
|
-
export class MessageBox {
|
|
5
|
-
constructor(screen) {
|
|
6
|
-
this.screen = screen;
|
|
7
|
-
|
|
8
|
-
this.component = blessed.box({
|
|
9
|
-
top: UI.TOP_BAR_HEIGHT,
|
|
10
|
-
left: 0,
|
|
11
|
-
width: '100%',
|
|
12
|
-
height: `100%-${UI.MESSAGE_BOX_OFFSET}`,
|
|
13
|
-
scrollable: true,
|
|
14
|
-
alwaysScroll: true,
|
|
15
|
-
keys: true,
|
|
16
|
-
vi: true,
|
|
17
|
-
mouse: true,
|
|
18
|
-
tags: true,
|
|
19
|
-
scrollbar: {
|
|
20
|
-
ch: UI.SCROLLBAR_CHAR,
|
|
21
|
-
style: {
|
|
22
|
-
bg: COLORS.BG_BLUE
|
|
23
|
-
}
|
|
24
|
-
},
|
|
25
|
-
style: {
|
|
26
|
-
fg: COLORS.FG_WHITE,
|
|
27
|
-
bg: COLORS.BG_BLACK
|
|
28
|
-
}
|
|
29
|
-
});
|
|
30
|
-
|
|
31
|
-
this.screen.append(this.component);
|
|
32
|
-
}
|
|
33
|
-
|
|
34
|
-
addMessage(formattedLine) {
|
|
35
|
-
const current = this.component.getContent();
|
|
36
|
-
const next = current && current.length > 0 ? `${current}\n${formattedLine}` : formattedLine;
|
|
37
|
-
this.component.setContent(next);
|
|
38
|
-
this.component.setScrollPerc(100);
|
|
39
|
-
this.screen.render();
|
|
40
|
-
}
|
|
41
|
-
|
|
42
|
-
scrollUp() {
|
|
43
|
-
this.component.scroll(-1);
|
|
44
|
-
this.screen.render();
|
|
45
|
-
}
|
|
46
|
-
|
|
47
|
-
scrollDown() {
|
|
48
|
-
this.component.scroll(1);
|
|
49
|
-
this.screen.render();
|
|
50
|
-
}
|
|
51
|
-
}
|
|
@@ -1,32 +0,0 @@
|
|
|
1
|
-
import blessed from 'blessed';
|
|
2
|
-
import { UI, COLORS } from '../../constants.js';
|
|
3
|
-
|
|
4
|
-
export class StatusBar {
|
|
5
|
-
constructor(screen) {
|
|
6
|
-
this.screen = screen;
|
|
7
|
-
|
|
8
|
-
this.component = blessed.box({
|
|
9
|
-
bottom: 0,
|
|
10
|
-
left: 0,
|
|
11
|
-
width: '100%',
|
|
12
|
-
height: UI.STATUS_BAR_HEIGHT,
|
|
13
|
-
content: ' Ready',
|
|
14
|
-
tags: true,
|
|
15
|
-
style: {
|
|
16
|
-
fg: COLORS.FG_WHITE,
|
|
17
|
-
bg: COLORS.BG_BLUE
|
|
18
|
-
}
|
|
19
|
-
});
|
|
20
|
-
|
|
21
|
-
this.screen.append(this.component);
|
|
22
|
-
}
|
|
23
|
-
|
|
24
|
-
update(message, type = 'info') {
|
|
25
|
-
let color = COLORS.INFO;
|
|
26
|
-
if (type === 'error') color = COLORS.ERROR;
|
|
27
|
-
if (type === 'success') color = COLORS.SUCCESS;
|
|
28
|
-
|
|
29
|
-
this.component.setContent(` {${color}}${message}{/${color}}`);
|
|
30
|
-
this.screen.render();
|
|
31
|
-
}
|
|
32
|
-
}
|
|
@@ -1,63 +0,0 @@
|
|
|
1
|
-
import blessed from 'blessed';
|
|
2
|
-
import { UI, COLORS, ICONS } from '../../constants.js';
|
|
3
|
-
import { parseRpcHost, formatTimestamp } from '../../utils.js';
|
|
4
|
-
|
|
5
|
-
export class TopBar {
|
|
6
|
-
constructor(screen, config, myAddress) {
|
|
7
|
-
this.screen = screen;
|
|
8
|
-
this.config = config;
|
|
9
|
-
this.myAddress = myAddress;
|
|
10
|
-
this.totalMessages = 0;
|
|
11
|
-
this.encryptionType = 'N/A';
|
|
12
|
-
|
|
13
|
-
this.component = blessed.box({
|
|
14
|
-
top: 0,
|
|
15
|
-
left: 0,
|
|
16
|
-
width: '100%',
|
|
17
|
-
height: UI.TOP_BAR_HEIGHT,
|
|
18
|
-
content: 'Loading...',
|
|
19
|
-
tags: true,
|
|
20
|
-
style: {
|
|
21
|
-
fg: COLORS.FG_WHITE,
|
|
22
|
-
bg: COLORS.BG_BLUE
|
|
23
|
-
}
|
|
24
|
-
});
|
|
25
|
-
|
|
26
|
-
this.screen.append(this.component);
|
|
27
|
-
}
|
|
28
|
-
|
|
29
|
-
update(status) {
|
|
30
|
-
const rpcUrl = parseRpcHost(this.config.rpc_url);
|
|
31
|
-
const connectedIndicator = status.connected ?
|
|
32
|
-
`{${COLORS.CONNECTED}}${ICONS.CONNECTED}{/${COLORS.CONNECTED}}` :
|
|
33
|
-
`{${COLORS.DISCONNECTED}}${ICONS.DISCONNECTED}{/${COLORS.DISCONNECTED}}`;
|
|
34
|
-
|
|
35
|
-
const lastPollStr = status.lastPoll ?
|
|
36
|
-
formatTimestamp(status.lastPoll, this.config.timezone) :
|
|
37
|
-
'--:--:--';
|
|
38
|
-
|
|
39
|
-
// Format timezone display
|
|
40
|
-
let timezoneDisplay = this.config.timezone || 'UTC';
|
|
41
|
-
if (timezoneDisplay !== 'UTC') {
|
|
42
|
-
if (!timezoneDisplay.startsWith('+') && !timezoneDisplay.startsWith('-')) {
|
|
43
|
-
timezoneDisplay = `+${timezoneDisplay}`;
|
|
44
|
-
}
|
|
45
|
-
timezoneDisplay = `UTC${timezoneDisplay}`;
|
|
46
|
-
}
|
|
47
|
-
|
|
48
|
-
this.component.setContent(
|
|
49
|
-
`Neurai DePIN | ${connectedIndicator} RPC: ${rpcUrl} | Token: ${this.config.token} | Time: ${timezoneDisplay}\n` +
|
|
50
|
-
`Address: ${this.myAddress} | Total: ${this.totalMessages} | Encryption: ${this.encryptionType} | Last poll: ${lastPollStr}`
|
|
51
|
-
);
|
|
52
|
-
|
|
53
|
-
this.screen.render();
|
|
54
|
-
}
|
|
55
|
-
|
|
56
|
-
setTotalMessages(count) {
|
|
57
|
-
this.totalMessages = count;
|
|
58
|
-
}
|
|
59
|
-
|
|
60
|
-
setEncryptionType(type) {
|
|
61
|
-
this.encryptionType = type;
|
|
62
|
-
}
|
|
63
|
-
}
|