hedgequantx 1.7.6 → 1.7.8

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/src/pages/user.js CHANGED
@@ -1,55 +1,45 @@
1
1
  /**
2
- * @fileoverview User info page
3
- * @module pages/user
2
+ * User info page
4
3
  */
5
4
 
6
5
  const chalk = require('chalk');
7
6
  const ora = require('ora');
8
- const inquirer = require('inquirer');
9
7
 
10
8
  const { connections } = require('../services');
11
9
  const { getLogoWidth, getColWidths, drawBoxHeader, drawBoxFooter, draw2ColHeader, visibleLength, padText } = require('../ui');
10
+ const { prompts } = require('../utils');
12
11
 
13
12
  /**
14
- * Shows user information
15
- * @param {Object} service - Current service
13
+ * Show user info
16
14
  */
17
15
  const showUserInfo = async (service) => {
18
16
  const spinner = ora({ text: 'Fetching user info...', color: 'yellow' }).start();
19
17
  const boxWidth = getLogoWidth();
20
18
  const { col1, col2 } = getColWidths(boxWidth);
21
19
 
22
- // Helper for row formatting
23
20
  const fmtRow = (label, value, colW) => {
24
21
  const labelStr = ' ' + label.padEnd(14);
25
22
  const valueVisible = visibleLength(value || '');
26
- const totalVisible = labelStr.length + valueVisible;
27
- const padding = Math.max(0, colW - totalVisible);
23
+ const padding = Math.max(0, colW - labelStr.length - valueVisible);
28
24
  return chalk.white(labelStr) + value + ' '.repeat(padding);
29
25
  };
30
26
 
31
- // Get user info
32
27
  let userInfo = null;
33
28
  let accountCount = 0;
34
29
 
35
- if (service && service.user) {
30
+ if (service?.user) {
36
31
  userInfo = service.user;
37
32
  } else if (service) {
38
33
  const result = await service.getUser();
39
- if (result.success) {
40
- userInfo = result.user;
41
- }
34
+ if (result.success) userInfo = result.user;
42
35
  }
43
36
 
44
- // Get account count
45
37
  if (connections.count() > 0) {
46
38
  const accounts = await connections.getAllAccounts();
47
39
  accountCount = accounts.length;
48
40
  } else if (service) {
49
41
  const result = await service.getTradingAccounts();
50
- if (result.success) {
51
- accountCount = result.accounts.length;
52
- }
42
+ if (result.success) accountCount = result.accounts.length;
53
43
  }
54
44
 
55
45
  spinner.succeed('User info loaded');
@@ -62,21 +52,17 @@ const showUserInfo = async (service) => {
62
52
  } else {
63
53
  draw2ColHeader('PROFILE', 'CONNECTIONS', boxWidth);
64
54
 
65
- // Username
66
55
  const username = userInfo.userName || userInfo.username || 'Unknown';
67
56
  const connCount = connections.count() || 1;
68
57
  console.log(chalk.cyan('║') + fmtRow('Username:', chalk.cyan(username.toUpperCase()), col1) + chalk.cyan('│') + fmtRow('Connections:', chalk.cyan(connCount.toString()), col2) + chalk.cyan('║'));
69
58
 
70
- // Email
71
59
  const email = userInfo.email || 'N/A';
72
60
  console.log(chalk.cyan('║') + fmtRow('Email:', chalk.white(email), col1) + chalk.cyan('│') + fmtRow('Accounts:', chalk.cyan(accountCount.toString()), col2) + chalk.cyan('║'));
73
61
 
74
- // User ID
75
62
  const userId = userInfo.userId || userInfo.id || 'N/A';
76
63
  const platform = service.propfirm?.name || 'ProjectX';
77
64
  console.log(chalk.cyan('║') + fmtRow('User ID:', chalk.gray(userId.toString()), col1) + chalk.cyan('│') + fmtRow('Platform:', chalk.magenta(platform), col2) + chalk.cyan('║'));
78
65
 
79
- // First/Last Name
80
66
  const firstName = userInfo.firstName || '';
81
67
  const lastName = userInfo.lastName || '';
82
68
  const fullName = (firstName + ' ' + lastName).trim() || 'N/A';
@@ -86,7 +72,7 @@ const showUserInfo = async (service) => {
86
72
  drawBoxFooter(boxWidth);
87
73
  console.log();
88
74
 
89
- await inquirer.prompt([{ type: 'input', name: 'c', message: 'Press Enter to continue...' }]);
75
+ await prompts.waitForEnter();
90
76
  };
91
77
 
92
78
  module.exports = { showUserInfo };
@@ -3,5 +3,6 @@
3
3
  */
4
4
 
5
5
  const { logger, LEVELS } = require('./logger');
6
+ const prompts = require('./prompts');
6
7
 
7
- module.exports = { logger, LEVELS };
8
+ module.exports = { logger, LEVELS, prompts };
@@ -0,0 +1,195 @@
1
+ /**
2
+ * Centralized prompts utility - lightweight, no external UI
3
+ * Uses readline for simple input, keeps our custom box design
4
+ */
5
+
6
+ const readline = require('readline');
7
+
8
+ /**
9
+ * Create readline interface
10
+ */
11
+ const createRL = () => readline.createInterface({
12
+ input: process.stdin,
13
+ output: process.stdout
14
+ });
15
+
16
+ /**
17
+ * Wait for Enter key
18
+ */
19
+ const waitForEnter = (message = 'Press Enter to continue...') => new Promise(resolve => {
20
+ const rl = createRL();
21
+ rl.question(message, () => { rl.close(); resolve(); });
22
+ });
23
+
24
+ /**
25
+ * Simple text input
26
+ */
27
+ const textInput = (message, defaultVal = '') => new Promise(resolve => {
28
+ const rl = createRL();
29
+ const prompt = defaultVal ? `${message} (${defaultVal}): ` : `${message}: `;
30
+ rl.question(prompt, (answer) => {
31
+ rl.close();
32
+ resolve(answer.trim() || defaultVal);
33
+ });
34
+ });
35
+
36
+ /**
37
+ * Password input (hidden)
38
+ */
39
+ const passwordInput = (message) => new Promise(resolve => {
40
+ const rl = createRL();
41
+ process.stdout.write(`${message}: `);
42
+
43
+ if (process.stdin.isTTY) {
44
+ process.stdin.setRawMode(true);
45
+ }
46
+
47
+ let password = '';
48
+ const onData = (char) => {
49
+ char = char.toString();
50
+
51
+ if (char === '\n' || char === '\r') {
52
+ if (process.stdin.isTTY) process.stdin.setRawMode(false);
53
+ process.stdin.removeListener('data', onData);
54
+ console.log();
55
+ rl.close();
56
+ resolve(password);
57
+ } else if (char === '\u0003') { // Ctrl+C
58
+ if (process.stdin.isTTY) process.stdin.setRawMode(false);
59
+ process.stdin.removeListener('data', onData);
60
+ rl.close();
61
+ resolve(null);
62
+ } else if (char === '\u007F' || char === '\b') { // Backspace
63
+ if (password.length > 0) {
64
+ password = password.slice(0, -1);
65
+ process.stdout.write('\b \b');
66
+ }
67
+ } else {
68
+ password += char;
69
+ process.stdout.write('*');
70
+ }
71
+ };
72
+
73
+ process.stdin.on('data', onData);
74
+ process.stdin.resume();
75
+ });
76
+
77
+ /**
78
+ * Select from options using arrow keys
79
+ */
80
+ const selectOption = (message, options) => new Promise(resolve => {
81
+ if (!process.stdin.isTTY) {
82
+ // Fallback for non-TTY
83
+ const rl = createRL();
84
+ console.log(message);
85
+ options.forEach((opt, i) => console.log(` ${i + 1}. ${opt.label}`));
86
+ rl.question('Enter number: ', (answer) => {
87
+ rl.close();
88
+ const idx = parseInt(answer) - 1;
89
+ resolve(options[idx]?.value || null);
90
+ });
91
+ return;
92
+ }
93
+
94
+ let selectedIndex = 0;
95
+ const maxIndex = options.length - 1;
96
+
97
+ const render = () => {
98
+ // Move cursor up and clear lines
99
+ if (selectedIndex > 0 || options.length > 1) {
100
+ process.stdout.write(`\x1B[${options.length}A`);
101
+ }
102
+
103
+ options.forEach((opt, i) => {
104
+ const prefix = i === selectedIndex ? '› ' : ' ';
105
+ const style = i === selectedIndex ? '\x1B[36m' : '\x1B[90m'; // cyan : gray
106
+ process.stdout.write(`\x1B[2K${style}${prefix}${opt.label}\x1B[0m\n`);
107
+ });
108
+ };
109
+
110
+ // Initial render
111
+ console.log(`\x1B[36m${message}\x1B[0m`);
112
+ options.forEach((opt, i) => {
113
+ const prefix = i === selectedIndex ? '› ' : ' ';
114
+ const style = i === selectedIndex ? '\x1B[36m' : '\x1B[90m';
115
+ console.log(`${style}${prefix}${opt.label}\x1B[0m`);
116
+ });
117
+
118
+ readline.emitKeypressEvents(process.stdin);
119
+ process.stdin.setRawMode(true);
120
+ process.stdin.resume();
121
+
122
+ const onKeypress = (str, key) => {
123
+ if (key.name === 'up' || key.name === 'k') {
124
+ selectedIndex = selectedIndex > 0 ? selectedIndex - 1 : maxIndex;
125
+ render();
126
+ } else if (key.name === 'down' || key.name === 'j') {
127
+ selectedIndex = selectedIndex < maxIndex ? selectedIndex + 1 : 0;
128
+ render();
129
+ } else if (key.name === 'return') {
130
+ cleanup();
131
+ resolve(options[selectedIndex].value);
132
+ } else if (key.name === 'escape' || (key.ctrl && key.name === 'c')) {
133
+ cleanup();
134
+ resolve(null);
135
+ } else if (str >= '1' && str <= '9') {
136
+ const idx = parseInt(str) - 1;
137
+ if (idx <= maxIndex) {
138
+ cleanup();
139
+ resolve(options[idx].value);
140
+ }
141
+ }
142
+ };
143
+
144
+ const cleanup = () => {
145
+ process.stdin.removeListener('keypress', onKeypress);
146
+ if (process.stdin.isTTY) process.stdin.setRawMode(false);
147
+ // Clear the menu display
148
+ process.stdout.write(`\x1B[${options.length + 1}A`);
149
+ for (let i = 0; i <= options.length; i++) {
150
+ process.stdout.write('\x1B[2K\n');
151
+ }
152
+ process.stdout.write(`\x1B[${options.length + 1}A`);
153
+ };
154
+
155
+ process.stdin.on('keypress', onKeypress);
156
+ });
157
+
158
+ /**
159
+ * Confirm yes/no
160
+ */
161
+ const confirmPrompt = (message, defaultVal = true) => new Promise(resolve => {
162
+ const rl = createRL();
163
+ const hint = defaultVal ? '(Y/n)' : '(y/N)';
164
+ rl.question(`${message} ${hint}: `, (answer) => {
165
+ rl.close();
166
+ const a = answer.toLowerCase().trim();
167
+ if (a === '') resolve(defaultVal);
168
+ else if (a === 'y' || a === 'yes') resolve(true);
169
+ else if (a === 'n' || a === 'no') resolve(false);
170
+ else resolve(defaultVal);
171
+ });
172
+ });
173
+
174
+ /**
175
+ * Number input
176
+ */
177
+ const numberInput = (message, defaultVal = 1, min = 1, max = 1000) => new Promise(resolve => {
178
+ const rl = createRL();
179
+ rl.question(`${message} (${defaultVal}): `, (answer) => {
180
+ rl.close();
181
+ const n = parseInt(answer) || defaultVal;
182
+ if (n < min) resolve(min);
183
+ else if (n > max) resolve(max);
184
+ else resolve(n);
185
+ });
186
+ });
187
+
188
+ module.exports = {
189
+ waitForEnter,
190
+ selectOption,
191
+ textInput,
192
+ passwordInput,
193
+ confirmPrompt,
194
+ numberInput
195
+ };