UNPKG

@dilapidated-penguin/cubetimer

Version:

fast and lightweight CLI timer for speedcubing. Track your solves, get random scrambles, and analyze your times

977 lines 42.6 kB
#! /usr/bin/env node "use strict"; var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { if (k2 === undefined) k2 = k; var desc = Object.getOwnPropertyDescriptor(m, k); if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { desc = { enumerable: true, get: function() { return m[k]; } }; } Object.defineProperty(o, k2, desc); }) : (function(o, m, k, k2) { if (k2 === undefined) k2 = k; o[k2] = m[k]; })); var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) { Object.defineProperty(o, "default", { enumerable: true, value: v }); }) : function(o, v) { o["default"] = v; }); var __importStar = (this && this.__importStar) || (function () { var ownKeys = function(o) { ownKeys = Object.getOwnPropertyNames || function (o) { var ar = []; for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k; return ar; }; return ownKeys(o); }; return function (mod) { if (mod && mod.__esModule) return mod; var result = {}; if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]); __setModuleDefault(result, mod); return result; }; })(); var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } return new (P || (P = Promise))(function (resolve, reject) { function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } step((generator = generator.apply(thisArg, _arguments || [])).next()); }); }; var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); const chalk_1 = __importDefault(require("chalk")); const commander_1 = require("commander"); const events_json_1 = require("./events.json"); const get_windows_1 = require("get-windows"); const nice_table_1 = require("nice-table"); const prompts_1 = require("@inquirer/prompts"); const child_process_1 = require("child_process"); const storage = __importStar(require("./util/storage")); const settingsUtil = __importStar(require("./util/settings")); const colour_palette = __importStar(require("./util/colourPalette")); const sound_1 = require("./util/sound"); const loading_1 = require("./util/loading"); const nodeplotlib_1 = require("nodeplotlib"); const path_1 = __importDefault(require("path")); const readline = require('readline'); var Scrambow = require('scrambow').Scrambow; const cfonts = require('cfonts'); const title = __importStar(require("./cli-title.json")); const program = new commander_1.Command(); var saved_data = storage.loadData(); //main_window_id const main_window_id = (0, get_windows_1.activeWindowSync)().id; //timer variables********************************** let timer_running = false; let startTime = null; let space_been_pressed = false; let new_scramble = false; let solve_labelled = false; const node_global_key_listener_1 = require("@futpib/node-global-key-listener"); const timers_1 = require("timers"); const fs_1 = __importDefault(require("fs")); const listener = new node_global_key_listener_1.GlobalKeyboardListener(); //************************************************* //************************************************* if (title.previous_window !== main_window_id) { console.log(title.string); let res = JSON.parse(JSON.stringify(title)); res.previous_window = main_window_id; const title_path = path_1.default.join(__dirname, './cli-title.json'); fs_1.default.writeFileSync(title_path, JSON.stringify(res)); } program .version("1.0.40") .description("fast and lightweight CLI timer for speedcubing. Cstimer in the command line (in progress)"); program .command('graph') .argument('<property>', 'desired statistic to graph') //.option('-c, --console','Displays the graph in the console') .description(`generate a graph of one of the below stats: \n session_mean \n standard_deviation \n variance \n fastest_solve \n slowest_solve`) .action((property, options) => { const property_keys = ['fastest_solve', 'session_mean', 'standard_deviation', 'variance', 'slowest_solve', 'all']; function normalizeArg(arg) { const aliases = { fastest_solve: ['f', 'b', 'best', 'fast', 'fastest', 'fastest_time'], slowest_solve: ['w', 's', 'worst', 'slow', 'slowest', 'slowest_timer'], session_mean: ['m', 'mean', 'avg', 'average', 'session_mean'], standard_deviation: ['dev', 'standard_deviation', 'std.dev', 'deviation', 'd'], variance: ['var', 'v', 'variance', 'var.'], all: ['a'] }; for (const [key, val] of Object.entries(aliases)) { if ((key === arg) || (val.includes(arg))) { return key; } } return null; } const normalized_property = normalizeArg(property); if (normalized_property !== null) { const session_data = storage.loadStats().session_data; if (session_data.size >= 0) { const x_dates = Array.from(session_data.keys()) .map((ISO_date) => { return new Date(ISO_date); }); const retrieve_data = (property, console_option = true) => { const y_data = x_dates.map((date) => { return session_data.get(date.toISOString())[property]; }); return console_option ? { x: x_dates, y: y_data, } : { x: x_dates, y: y_data, type: 'scatter', name: property }; }; function consoleGraph(prop) { const allGraph = () => { function randomColor() { return [Math.random() * 255, Math.random() * 255, Math.random() * 255]; } const global_line = contrib.line({ xLabelPadding: 3, xPadding: 5, label: 'Graph', showLegend: true, width: '100%', height: '100%' }); screen.append(global_line); let global_data = []; const new_line = (prop) => { const { x, y } = retrieve_data(prop); const random_colour = randomColor(); const style = { line: random_colour, text: random_colour }; global_data.push({ x: x, y: y, title: prop, style: style }); }; property_keys.map((property) => { new_line(property); }); global_line.setData(global_data); }; const defaultGraph = () => { const line = contrib.line({ style: { line: "yellow", text: "green", baseline: "black" }, xLabelPadding: 3, xPadding: 5, label: `${normalized_property}(s)` }); let prop_data = retrieve_data(normalized_property); screen.append(line); //must append before setting data line.setData([prop_data]); }; let resGraph = (graphFunc) => { graphFunc(); screen.key(['escape', 'q', 'C-c'], function (ch, key) { return process.exit(0); }); screen.render(); }; return resGraph((prop === 'all') ? allGraph : defaultGraph); } if (options.console) { var blessed = require('blessed'), contrib = require('blessed-contrib'), screen = blessed.screen(); consoleGraph(normalized_property); } else { switch (normalized_property) { case 'all': const prop_keys = ['fastest_solve', 'session_mean', 'standard_deviation', 'variance', 'slowest_solve']; const data = prop_keys.map((property) => { const res = retrieve_data(property, false); return res; }); const layout = { title: 'Graph of all properties', xaxis: { title: 'sessions' }, yaxis: { title: 'times (s)' } }; (0, nodeplotlib_1.plot)(data, layout); break; default: const single_layout = { title: `Graph of ${normalized_property}`, xaxis: { title: 'sessions' }, yaxis: { title: 'times (s)' } }; (0, nodeplotlib_1.plot)([retrieve_data(normalized_property, false)], single_layout); break; } } } else { console.error(`error: ` + chalk_1.default.red(`There was no available session data`)); } } else { console.error(chalk_1.default.red(`${property}`) + ` is not a valid property. Below are the valid values`); console.log("session_mean \n" + "standard_deviation \n" + "variance \n" + "fastest_solve \n" + "slowest_solve"); } }); program .command('scramble') .argument('<format>', `Format of the scramble(s) you'd like to generate`) .argument('[number]', 'number of scrambles to generate', '1') .argument('[length]', `Length of the scramble`) .description('Generate a scramble') .action((event, count, length) => { var _a; const normalized_event = event .toLowerCase() .trim(); if (!validEvent(normalized_event)) { console.log(chalk_1.default.redBright(`invalid event`)); return; } count = count .toLowerCase() .trim(); const current_settings = settingsUtil.loadSettings(); const scramble_length = (_a = Number(length)) !== null && _a !== void 0 ? _a : current_settings.scramble_length; if ((scramble_length <= 0) || (scramble_length > 40)) { console.log(chalk_1.default.red(`invalid length`)); return; } var scramble_generator = new Scrambow(); const get_scramble_string = (count) => __awaiter(void 0, void 0, void 0, function* () { return scramble_generator .setType(normalized_event) .setLength(scramble_length) .get(Number(count)) .map((scramble_object, index) => { return `${index + 1}) ${stylizeScramble(scramble_object.scramble_string)}`; }) .join(`\n`); }); (0, loading_1.startLoader)(); get_scramble_string(count).then((scramble_string) => { console.log(scramble_string); }).catch((err) => { console.log(err); }).finally(() => { (0, loading_1.endLoader)(); }); }); program .command('start') .argument('[event]', 'the event you wish to practice', '333') .option('-f, --focusMode', 'Displays only the most important stats') .option('-w, --window', 'Opens a second command prompt window to display the informationa and stats related to the solve') .option('-i,--inspect', 'add inspection time') .description('Begin a session of practicing a certain event') .action((event, options) => { if (event !== undefined) { const normalized_event = event .toLowerCase() .trim(); if (validEvent(normalized_event)) { startSession(normalized_event, options); } else { console.error(chalk_1.default.red(`${event} is not a valid/supported event`)); } } else { (0, prompts_1.select)({ message: 'Select an event', choices: events_json_1.event_choices }) .then((event_choice) => { startSession(event_choice, options); }).catch((error) => { console.error(chalk_1.default.bgRed(`An error occurred`)); }); } }); program .command('metronome') .argument('[bpm]', 'the bpm of the metronome', settingsUtil.loadSettings().default_bpm) .description('start a metronome') .action((bpm) => { function metronome(bpm) { const interval = 60000 / bpm; const file_path = path_1.default.join(__dirname, `/sounds/${settingsUtil.loadSettings().default_metronome}`); setInterval(() => { (0, sound_1.playAudioFile)(file_path); }, interval); } const bpm_number = Number(bpm); if (isNaN(bpm_number)) { console.error(chalk_1.default.red(bpm) + ` is not a number`); return; } if ((bpm_number < 3) || (bpm_number > 180)) { console.error(chalk_1.default.red('the bpm must be between 3 and 180 beats per minute')); return; } console.log(`bpm: ` + chalk_1.default.bold(bpm)); console.log(`Use ` + chalk_1.default.bold(`Ctrl + C`) + ` to exit the metronome`); metronome(bpm_number); }); program .command("settings") .argument("[property]", 'configure the cli to your liking') .description('configure the cli to your liking') .action((setting_to_change) => { let current_settings = settingsUtil.loadSettings(); const settings_list = Object.keys(current_settings); if (setting_to_change === undefined) { console.log(chalk_1.default.green(`Configure any of the below to some new and preferred value`)); console.table(current_settings); (0, prompts_1.select)({ message: "Select the setting you'd like to alter", choices: settings_list }).then((answer) => { updateSetting(current_settings, answer); }); } else { if (settings_list.indexOf(setting_to_change) !== -1) { updateSetting(current_settings, setting_to_change); } else { console.error(chalk_1.default.red('Invalid argument:' + chalk_1.default.white('The argument is not a setting to change'))); } } }); program .command('show-session') .description(`Shows a list of session date markers`) .action(() => { const menu_length = 5; function newChoices(menu_page) { const session_array = Array.from(storage.loadData().data.values()); let menu_choices = session_array .map((session) => { return { name: session.date_formatted, value: session.date }; }).filter((v, index) => { return (index >= menu_page * (menu_length)) && (index < ((menu_page + 1) * menu_length)); }); if (menu_page !== 0) { menu_choices.unshift({ name: chalk_1.default.blue(`Back`), value: 'back' }); } if (session_array[(menu_page + 1) * menu_length] !== undefined) { menu_choices.push({ name: chalk_1.default.blue(`next`), value: 'next' }); } (0, prompts_1.select)({ message: `Select the session you'd like to observe`, choices: menu_choices }).then((value) => { switch (value) { case 'back': newChoices(menu_page - 1); break; case 'next': newChoices(menu_page + 1); break; default: const current_session_data = storage.loadData().data.get(value); let info_table = current_session_data.entries.map((instance, index) => { const label = (instance.label === "DNF") ? chalk_1.default.red(instance.label) : instance.label; return { n: index + 1, time: instance.time.toFixed(settingsUtil.loadSettings().sig_fig), label: label !== null && label !== void 0 ? label : chalk_1.default.green('OK'), }; }); console.log(`\n`); console.log((0, nice_table_1.createTable)(info_table, ['n', 'time', 'label'])); const current_sesssion_stats = storage.loadStats().session_data.get(value); if (current_sesssion_stats !== undefined) { console.log(`${chalk_1.default.green(current_session_data.date_formatted)}\n Event:${current_session_data.event} \n`); console.log(Object.keys(current_sesssion_stats).map((stat_name) => { return `${chalk_1.default.yellowBright(stat_name)}: ${current_sesssion_stats[stat_name]}${chalk_1.default.green('s')} \n`; }).join('')); } else { console.log(`Statistics unavailable`); } break; } }).catch((err) => { console.error(chalk_1.default.red(`An error has occurred:${err}`)); }); } newChoices(0); }); program.parse(process.argv); function updateSetting(current_settings, property) { switch (property) { case 'default_metronome': const fs = require('node:fs'); const sounds_path = path_1.default.join(__dirname, `./sounds`); let sound_names = fs.readdirSync(sounds_path); const current_sound_index = sound_names.findIndex(u => u === current_settings.default_metronome); sound_names[current_sound_index] = chalk_1.default.bold(sound_names[current_sound_index]); (0, prompts_1.select)({ message: `Select the sound of the metronome`, choices: sound_names }).then((sound_name) => { current_settings.default_metronome = sound_name; settingsUtil.saveSettings(current_settings); console.log(chalk_1.default.green(`Metronome sound setting updated`)); }).catch((err) => { console.error(err); }); break; default: let prompt; switch (typeof current_settings[property]) { case 'number': prompt = prompts_1.number; case 'string': prompt = prompts_1.input; } prompt({ message: `Enter new value for ${property}`, default: current_settings[property] }).then((new_value) => { const num_value = Number(new_value); if (!isNaN(num_value)) { current_settings[property] = num_value; } else { current_settings[property] = `${new_value}`; } settingsUtil.saveSettings(current_settings); console.log(chalk_1.default.green('settings updated!')); console.table(current_settings); }); break; } } function validEvent(event_to_check) { return (events_json_1.events_list.indexOf(event_to_check) !== -1); } function startSession(event, options) { console.clear(); const session = Date.now(); const session_date = new Date(session); const session_date_ISO = session_date.toISOString(); cfonts.say(`session: ${session}`, { font: 'tiny', // define the font face align: 'center', // define text alignment colors: ['magenta'], background: 'transparent', // define the background color, you can also use `backgroundColor` here as key letterSpacing: 1, // define letter spacing }); const current_settings = settingsUtil.loadSettings(); saved_data.data.set(session_date_ISO, storage.newSessionLog(session_date, event)); saved_data.last_accessed_log = session_date_ISO; storage.saveData(saved_data); new_scramble = true; listener.kill(); if (options.window) { const scriptPath = path_1.default.join(__dirname, 'window.js'); const cmd = (0, child_process_1.spawn)('cmd.exe', ['/K', `start cmd /K node ${scriptPath} ${session_date.toISOString()}`], { detached: true, stdio: 'ignore', windowsHide: false }); cmd.unref(); // Allow the parent process to exit without waiting for this new process cmd.on('error', (err) => console.error(`Process error: ${err.message}`)); } newSolve(current_settings, event, session_date, options); } function newSolve(current_settings, event, session_date, option) { const session_date_ISO = session_date.toISOString(); var scramble_generator = new Scrambow(); let lines_after_counter = 0; let scramble = scramble_generator .setType(event) .setLength(current_settings.scramble_length) .get(1)[0] .scramble_string; process.stdout.write(`\x1b[2K\r`); console.log(chalk_1.default.bold.red(`Scramble:`)); process.stdout.write("\x1b[2K"); console.log(stylizeScramble(scramble) + '\n'); const pressedState = () => { space_been_pressed = true; belowCounter(chalk_1.default.bgRed('...')); }; const belowCounter = (text) => { readline.clearLine(process.stdout, 0); readline.cursorTo(process.stdout, 0); process.stdout.write(`${text}\n`); readline.cursorTo(process.stdout, 0); lines_after_counter++; return lines_after_counter; }; function newSolvePrompt() { console.log(`\n`); console.log(chalk_1.default.dim(`To label/delete the last solve simply use (`) + chalk_1.default.italic.yellow(`e`) + chalk_1.default.dim(`/`) + chalk_1.default.italic.yellow(`d`) + chalk_1.default.dim(`) respectively`)); console.log(chalk_1.default.dim(`Exit session mode using`), chalk_1.default.green(`Ctrl+C`) + `\n`); console.log(chalk_1.default.bold.magentaBright(`Whenever ready use the spacebar to start a new solve using`) + chalk_1.default.italic.yellow(` n`) + `\n \n`); } function inspection_time(inspection_time = 15) { let count = -1; space_been_pressed = false; let timer_started = false; let intervalid; belowCounter(`tap the ${chalk_1.default.underline(`Spacebar`)} to start the inspection timer`); listener.addListener(function (e, down) { if ((e.name === "SPACE")) { if (e.state === "DOWN") { if (timer_started) { if (!space_been_pressed) { pressedState(); } } } else { if (!timer_started) { timer_started = true; startInspectionTimer(); belowCounter(chalk_1.default.underline(`inspection started`)); } if (space_been_pressed && timer_started) { (0, timers_1.clearInterval)(intervalid); listener.kill(); new_scramble = true; space_been_pressed = true; solve_labelled = false; startListener(current_settings, event, session_date, option); } } } }); function startInspectionTimer() { intervalid = global.setInterval(() => { count++; const colour_gradient = 1 - ((inspection_time - count) / inspection_time); const red = (gradient) => { return Math.round(255 * gradient); }; const green = (gradient) => { return Math.round(-255 * (gradient) + 255); }; let colour = chalk_1.default.rgb(red(colour_gradient), green(colour_gradient), 0); //udpate the timer const updateTimer = (time, lines_after_counter) => { readline.cursorTo(process.stdout, 0); readline.moveCursor(process.stdout, 0, -lines_after_counter - 1); readline.clearLine(process.stdout, 0); process.stdout.write(`${colour(`${time - count}`)}`); readline.moveCursor(process.stdout, 0, lines_after_counter + 1); readline.cursorTo(process.stdout, 0); }; updateTimer(inspection_time, lines_after_counter); if (count >= inspection_time) { if (count = inspection_time) { listener.kill(); (0, timers_1.clearInterval)(intervalid); newSolvePrompt(); listener.kill(); new_scramble = true; solve_labelled = false; space_been_pressed = false; newSolve(current_settings, event, session_date, option); } } }, 1000); } } if (option.inspect) { inspection_time(current_settings.inspection_sec); } else { startListener(current_settings, event, session_date, option); } function startListener(current_settings, event, session_date, option) { const releasedState = () => { space_been_pressed = false; readline.moveCursor(process.stdout, 0, -(option.inspect ? 1 : 2)); readline.cursorTo(process.stdout, 0); readline.clearLine(process.stdout, 0); process.stdout.write(chalk_1.default.bgGreenBright('SOLVE') + '\n \n'); readline.cursorTo(process.stdout, 0); startTimer(); }; if (option.inspect) { if (space_been_pressed) { releasedState(); } } listener.addListener(function (e, down) { var _a; const edit_selected = (date_ISO) => { (0, prompts_1.number)({ message: `Enter the index of the solve you'd like to change`, default: 1 }).then((index_to_alter) => { const current_session = saved_data.data.get(date_ISO).entries; if ((index_to_alter < 0) || (index_to_alter > current_session.length)) { console.log(chalk_1.default.red('invalid index')); return; } const selected_entry = current_session.at(index_to_alter); console.log(Object.keys(selected_entry).map((key) => { return `${key}: ${chalk_1.default.green(selected_entry[key])}`; }) .join('')); (0, prompts_1.select)({ message: `Label or delete`, choices: ['label', 'delete'] }).then((answer) => { switch (answer) { case 'label': editEntry(date_ISO, index_to_alter); break; case 'delete': deleteEntry(date_ISO, index_to_alter); break; } }).catch((err) => { console.log(err); }); }).catch((err) => { console.log(err); }); }; const deleteEntry = (date_ISO, index_to_delete = null) => { let current_session = saved_data.data.get(date_ISO); if (current_session.entries.length >= 1) { if (index_to_delete === null) { current_session.entries.pop(); console.log(chalk_1.default.blue(`Last solve deleted`)); } else { current_session.entries = current_session.entries.filter((d, index) => index !== index_to_delete); console.log(`Session ${index_to_delete} deleted`); } saved_data.data.set(date_ISO, current_session); storage.saveData(saved_data); } else { console.error(chalk_1.default.red(`There exist no entries in the current session to delete`)); } }; const editEntry = (date_ISO, index_to_edit = null) => { const current_session = saved_data.data.get(date_ISO); if (current_session.entries.length >= 1) { const editing_last_solve = index_to_edit === null; const entry_message = editing_last_solve ? `Select the label for the previous solve` : `Select the label for solve #${index_to_edit}`; (0, prompts_1.select)({ message: entry_message, choices: [ '+3', 'DNF', 'OK' ] }).then((answer) => { const index = editing_last_solve ? -1 : index_to_edit + 1; current_session.entries.at(index).label = answer; saved_data.data.set(date_ISO, current_session); storage.saveData(saved_data); console.log(chalk_1.default.green(`Last solve labelled ${answer}`)); console.log(chalk_1.default.bold.magentaBright(`Whenever ready use the spacebar to start a new solve`)); }).catch((err) => { console.log(chalk_1.default.red(`An error has occurred:${err}`)); }); } else { console.log(chalk_1.default.redBright(`There exist no entries in the current session to label`)); } }; if (((_a = (0, get_windows_1.activeWindowSync)()) === null || _a === void 0 ? void 0 : _a.id) !== main_window_id) { return; } if ((e.name === "D") && (e.state === "UP") && (!new_scramble)) { deleteEntry(session_date_ISO); return; } if ((e.name === "N") && (e.state === "UP")) { if (!new_scramble) { process.stdout.write('\x1b[2K'); listener.kill(); new_scramble = true; solve_labelled = false; space_been_pressed = false; newSolve(current_settings, event, session_date, option); } return; } if ((e.name === "E") && (e.state === "UP") && (!new_scramble)) { if (!solve_labelled) { solve_labelled = true; console.log(`\n \n`); editEntry(session_date_ISO); } else { console.log(chalk_1.default.redBright(`The solve has already been labelled.`)); } return; } if ((e.name === "SPACE") && (new_scramble)) { if (!timer_running) { if (e.state === "DOWN") { if (!space_been_pressed) { pressedState(); process.stdout.write("\n"); } else { process.stdout.write("\b \b"); } } else { if (space_been_pressed) { releasedState(); } } } else { if (e.state === "DOWN") { const elapsedTime = stopTimer(); new_scramble = false; const current_session = saved_data.data.get(session_date_ISO); current_session.entries.push({ scramble: scramble, time: elapsedTime, label: null }); const session_average = current_session .entries .reduce((acc, curr) => { return acc += curr.time; }, 0) / current_session.entries.length; const best_time = current_session .entries .reduce((acc, curr) => { if (acc < curr.time) { return acc; } else { return curr.time; } }, Infinity); const worst_time = current_session .entries .reduce((acc, curr) => { if (acc > curr.time) { return acc; } else { return curr.time; } }, -Infinity); const variance = current_session .entries .reduce((acc, curr) => { return acc += Math.pow((session_average - curr.time), 2); }, 0) / current_session.entries.length; const stats_data = storage.loadStats(); const current_stats = { session_mean: session_average, standard_deviation: Math.sqrt(variance), variance: variance, fastest_solve: best_time, slowest_solve: worst_time }; const current_Ao5 = storage.Ao5(current_session); const current_Ao12 = storage.Ao12(current_session); stats_data.session_data.set(session_date_ISO, current_stats); stats_data.pb_Ao5 = (current_Ao5 < stats_data.pb_Ao5) ? current_Ao5 : stats_data.pb_Ao5; stats_data.pb_Ao12 = (current_Ao12 < stats_data.pb_Ao12) ? current_Ao12 : stats_data.pb_Ao12; storage.saveStats(stats_data); saved_data.data.set(session_date_ISO, current_session); storage.saveData(saved_data); process.stdout.write("\b \b"); cfonts.say(`${elapsedTime.toFixed(2)}s`, { font: 'block', // define the font face align: 'center', // define text alignment colors: ['white'], background: 'transparent', // define the background color, you can also use `backgroundColor` here as key letterSpacing: 1, // define letter spacing }); process.stdout.write("\b \b"); const sig_fig = settingsUtil.loadSettings().sig_fig; console.log(chalk_1.default.bold(`Time: `) + elapsedTime.toFixed(sig_fig) + chalk_1.default.green('s') + `\n`); const round_average = (value) => { if (value === null) { return '--'; } else { return value.toFixed(sig_fig); } }; console.log(chalk_1.default.bold(`Ao5: `) + chalk_1.default.magenta(round_average(current_Ao5)) + chalk_1.default.green(`s`)); console.log(chalk_1.default.bold(`Ao12: `) + chalk_1.default.magenta(round_average(current_Ao12)) + chalk_1.default.green(`s`) + `\n \n`); if (!(option.focusMode) && !(option.window)) { //solves console.table((0, nice_table_1.createTable)(current_session.entries.map((instance) => { var _a; return { time: instance.time.toFixed(sig_fig), label: (_a = instance.label) !== null && _a !== void 0 ? _a : 'OK' }; }), ['time', 'label'])); //stats const generateStatString = (current_stats) => { const titles = ['average', 'std. dev.', 'variance', 'fastest', 'slowest']; return Object.keys(current_stats) .map((stat_name, index) => { return `${titles[index]}: ${chalk_1.default.bold(current_stats[stat_name].toFixed(sig_fig))}`; }) .join(chalk_1.default.blue(` | `)); }; console.log(generateStatString(current_stats) + `\n`); } newSolvePrompt(); //reset timer_running = false; startTime = null; space_been_pressed = false; } } return; } process.stdout.write('\x1b[2K\r'); }); } } function stopTimer() { if (!startTime) return; timer_running = false; const endTime = process.hrtime(startTime); return endTime[0] + endTime[1] / 1e9; } function startTimer() { startTime = process.hrtime(); timer_running = true; } function stylizeScramble(scramble, r = 133, g = 18, b = 0) { function rgb_to_hsl(r, g, b) { r /= 255; g /= 255; b /= 255; const max = Math.max(r, g, b), min = Math.min(r, g, b); let h = 0, s = 0, l = (max + min) / 2; if (max !== min) { const d = max - min; s = l > 0.5 ? d / (2 - max - min) : d / (max + min); switch (max) { case r: h = (g - b) / d + (g < b ? 6 : 0); break; case g: h = (b - r) / d + 2; break; case b: h = (r - g) / d + 4; break; } h *= 60; } return [Math.round(h), +(s * 100).toFixed(1), +(l * 100).toFixed(1)]; } function hsl_to_rgb(h, s, l) { s /= 100; l /= 100; const c = (1 - Math.abs(2 * l - 1)) * s; const x = c * (1 - Math.abs((h / 60) % 2 - 1)); const m = l - c / 2; let r = 0, g = 0, b = 0; if (0 <= h && h < 60) { r = c; g = x; b = 0; } else if (60 <= h && h < 120) { r = x; g = c; b = 0; } else if (120 <= h && h < 180) { r = 0; g = c; b = x; } else if (180 <= h && h < 240) { r = 0; g = x; b = c; } else if (240 <= h && h < 300) { r = x; g = 0; b = c; } else if (300 <= h && h < 360) { r = c; g = 0; b = x; } return [ Math.round((r + m) * 255), Math.round((g + m) * 255), Math.round((b + m) * 255) ]; } const [h, s, l] = rgb_to_hsl(r, g, b); const { complementary, fourth_hue, fifth_hue } = colour_palette.tetratic(h, s, l); const colorMap = { 'F': chalk_1.default.rgb(r, g, b).underline, 'R': chalk_1.default.rgb(...hsl_to_rgb(...complementary)), 'L': chalk_1.default.rgb(...hsl_to_rgb(...complementary)), 'U': chalk_1.default.rgb(...hsl_to_rgb(...fourth_hue)), 'D': chalk_1.default.rgb(...hsl_to_rgb(...fourth_hue)), "'": chalk_1.default.whiteBright, " ": chalk_1.default.whiteBright, '2': chalk_1.default.rgb(...hsl_to_rgb(...fifth_hue)), }; const res = scramble .trim() .split('') .map(char => { const stylize = colorMap[char] || chalk_1.default.rgb(r, g, b); return stylize(char); }); return res .join(''); } //# sourceMappingURL=index.js.map