-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathindex.js
executable file
·196 lines (184 loc) · 7.31 KB
/
index.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
#!/usr/bin/env node
// Import modules
import * as fs from 'fs';
import * as path from 'path';
import * as readline from 'node:readline';
import * as readlinePromises from 'node:readline/promises';
import { URL } from 'url';
import { fileURLToPath } from 'node:url';
import clear from 'console-cls';
import chalk from 'chalk';
import figlet from 'figlet';
// Import files
import { splits } from './src/Splits.js';
import { upload } from './src/SplitsIO.js';
import * as View from './src/Views.js';
// ESM __dirname
export const dirname = fileURLToPath(import.meta.url).replace('index.js', '');
// Readline
export const rl = readlinePromises.createInterface({
input: process.stdin,
output: process.stdout
});
export const status = {
"state": "splash",
"raceInfo": {}
};
// Read keypresses during process life
readline.emitKeypressEvents(rl.input);
rl.input.setRawMode(true);
// Handling keypresses
rl.input.on('keypress', async (str, key) => {
// To exit the program: ctrl+c or ctrl+d or esc
if ([`\x03`, `\x04`, `\x1B`].includes(key.sequence)) {
process.exit(1);
}
// Ignore keypresses
if (status.state === 'wait') {
return;
}
// Keys to accept...
// ...during plash screen
if (status.state === 'splash') {
if (key.name === 'n') {
View.create();
} else if (key.name === 'l') {
status.state = 'load-before';
clear();
console.log(`Press ${chalk.cyan('l')} for local file or ${chalk.cyan('s')} for splits.io`);
} else if (key.name === 'r') {
View.race();
} else if (key.name === 'h') {
View.help();
}
// ...during help screen
} else if (status.state === 'help') {
splash();
// ...when ready to load to the timer
} else if (status.state === 'ready') {
console.log(status.raceInfo);
if (Object.keys(status.raceInfo).length > 0) {
View.activeRace();
} else {
View.active();
}
// ...to determine how to load splits
} else if (status.state === 'load-before') {
if (key.name === 'l') {
View.load('local');
} else if (key.name === 's') {
View.load('splitsio');
}
// ...while the timer is active
} else if (status.state === 'timer') {
if (key.name === config.hotkeys.split) {
if (View.timer.timer.running === false && View.timer.timer.started === false) {
View.timer.start();
} else if ((View.timer.race === true && View.timer.lap < View.timer.segments.length - 1) || (View.timer.race === false && View.timer.lap < View.timer.segments.length)) {
View.timer.split();
if (View.timer.lap === View.timer.segments.length) {
status.state = 'timer-stop';
console.log(`\nPress...\n* ${chalk.cyan('r')} to reset the timer\n* ${chalk.cyan('g')} to save any new best segments\n* ${chalk.cyan('p')} to save the current run as a personal best\n* ${chalk.cyan('s')} to save the splits file locally\n* ${chalk.cyan('u')} to upload the splits file to splits.io\n* ${chalk.cyan('m')} to return to the main menu`);
}
}
} else if (key.name === config.hotkeys.undo) {
if (View.timer.timer.running === true && View.timer.lap !== 0) {
View.timer.undo();
}
} else if (key.name === config.hotkeys.skip) {
if (View.timer.timer.running === true && View.timer.lap < View.timer.segments.length - 1) {
View.timer.skip();
}
} else if (key.name === config.hotkeys.pause) {
View.timer.timer.pause();
} else if (key.name === config.hotkeys.reset) {
if (View.timer.timer.started) {
View.timer.timer.stop();
if (View.timer.segments.some(seg => seg.currSegment !== null && seg.currSegment < seg.bestSegment)) {
status.state = 'reset-check';
console.log(`\nDo you want to save your new best segments? Press ${chalk.cyan('y')} or ${chalk.cyan('n')}.`);
} else {
View.timer.reset();
}
}
} else if (key.name === config.hotkeys.quit) {
View.timer.timer.stop();
status.state = 'timer-stop';
console.log(`\nPress...\n* ${chalk.cyan('r')} to reset the timer\n* ${chalk.cyan('g')} to save any new best segments\n* ${chalk.cyan('p')} to save the current run as a personal best\n* ${chalk.cyan('s')} to save the splits file locally\n* ${chalk.cyan('u')} to upload the splits file to splits.io\n* ${chalk.cyan('m')} to return to the main menu`);
}
// ...when the timer is done
} else if (status.state === 'timer-stop') {
if (key.name === 'r') {
View.timer.reset();
status.state = 'timer';
} else if (key.name === 'g') {
View.timer.saveBests();
console.log('Best segments saved.')
} else if (key.name === 'p') {
View.timer.saveRun();
console.log('Run saved.')
} else if (key.name === 's') {
fs.writeFileSync(`${config.splitsPath}/${splits.fileName}.json`, JSON.stringify(splits, null, 4));
console.log('File saved.');
} else if (key.name === 'u') {
fs.writeFileSync(`${config.splitsPath}/${splits.fileName}.json`, JSON.stringify(splits, null, 4));
const resp = await upload();
console.log(resp);
} else if (key.name === 'm') {
splash();
}
// ...before reseting
} else if (status.state === 'reset-check') {
if (key.name === 'y') {
View.timer.saveBests();
View.timer.reset();
} else if (key.name === 'n') {
View.timer.reset();
}
}
});
// Default configuration file
const defaultConfig = {
"hotkeys": {
"split": "s",
"pause": "p",
"reset": "r",
"skip": "n",
"undo": "b",
"quit": "q"
},
"colors": {
"headers": "white",
"names": "white",
"times": "white",
"timer": "white",
"ahead": "green",
"behind": "red",
"best": "yellowBright"
},
"precision": {
"timer": "S.mm",
"splits": "M:SS",
"deltas": "S.m"
},
"splitsPath": path.resolve(dirname, 'splits')
}
// Check for config file
if (!fs.existsSync(path.resolve(dirname, 'config.json'))) {
fs.writeFileSync(path.resolve(dirname, 'config.json'), JSON.stringify(defaultConfig, null, 4));
}
export const config = JSON.parse(fs.readFileSync(path.resolve(dirname, 'config.json')));
// Check for splits folder
if (!fs.existsSync(path.resolve(dirname, 'splits')) && config.splitsPath === path.resolve(dirname, 'splits')) {
fs.mkdirSync(path.resolve(dirname, 'splits'));
}
// Splash screen
const splash = () => {
status.state = 'splash';
clear();
console.log(chalk.green(figlet.textSync('chronode', { font: "Speed" })));
console.log(`Version 0.0.7`);
console.log(`\nPress...\n* ${chalk.cyan('n')} to create new splits\n* ${chalk.cyan('l')} to load existing splits\n* ${chalk.cyan('r')} to connect to a race on racetime.gg\n* ${chalk.cyan('h')} for help`);
console.log(`\nYou can exit any time by pressing ${chalk.cyan('esc')}`);
}
splash();