typing-game-cli 1.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/dist/app.js +104 -0
- package/dist/cli.js +43 -0
- package/dist/constants.js +2 -0
- package/dist/helpers.js +43 -0
- package/dist/sentences/ambrose-bierce/moxon-s-master.json +24 -0
- package/dist/sentences/ambrose-bierce/on-a-mountain.json +13 -0
- package/dist/sentences/ambrose-bierce/the-spook-house.json +14 -0
- package/dist/sentences/ambrose-bierce/the-thing-at-nolan.json +13 -0
- package/dist/sentences/mark-twain/is-he-living-or-is-he-dead.json +16 -0
- package/package.json +101 -0
- package/readme.md +44 -0
package/dist/app.js
ADDED
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import { Text, Box, useInput, useStdout } from 'ink';
|
|
3
|
+
import TextInput from 'ink-text-input-2';
|
|
4
|
+
import { Spinner, Alert } from '@inkjs/ui';
|
|
5
|
+
import Gradient from 'ink-gradient';
|
|
6
|
+
import { proxy, useSnapshot } from 'valtio';
|
|
7
|
+
import random from 'just-random';
|
|
8
|
+
import { getBorderColor, getDefaultSuite, getRobotBorderColor, getStatusVariant, getMessage, isFinished, getIntervalMs } from './helpers.js';
|
|
9
|
+
import { optionKeyColor } from './constants.js';
|
|
10
|
+
const state = proxy({
|
|
11
|
+
status: 'PAUSED',
|
|
12
|
+
suite: getDefaultSuite(),
|
|
13
|
+
source: null,
|
|
14
|
+
robotText: '',
|
|
15
|
+
userText: '',
|
|
16
|
+
intervalId: null,
|
|
17
|
+
level: 'medium'
|
|
18
|
+
});
|
|
19
|
+
export default function App({
|
|
20
|
+
robotLevel
|
|
21
|
+
}) {
|
|
22
|
+
const snap = useSnapshot(state);
|
|
23
|
+
const {
|
|
24
|
+
stdout
|
|
25
|
+
} = useStdout();
|
|
26
|
+
useInput((input, _key) => {
|
|
27
|
+
if (input === 'y') {
|
|
28
|
+
state.userText = '';
|
|
29
|
+
state.robotText = '';
|
|
30
|
+
state.status = 'RUNNING';
|
|
31
|
+
state.source = random(state.suite.sentences);
|
|
32
|
+
state.level = robotLevel || 'medium';
|
|
33
|
+
const interval = setInterval(() => {
|
|
34
|
+
state.robotText += state.source.slice(state.robotText.length, state.robotText.length + 1);
|
|
35
|
+
if (state.robotText === state.source) {
|
|
36
|
+
state.status = 'LOST';
|
|
37
|
+
clearInterval(interval);
|
|
38
|
+
} else if (state.userText === state.source) {
|
|
39
|
+
state.status = 'WON';
|
|
40
|
+
clearInterval(interval);
|
|
41
|
+
}
|
|
42
|
+
}, getIntervalMs(state.level));
|
|
43
|
+
state.intervalId = interval;
|
|
44
|
+
}
|
|
45
|
+
}, {
|
|
46
|
+
isActive: state.status !== 'RUNNING'
|
|
47
|
+
});
|
|
48
|
+
return snap.status === 'PAUSED' ? /*#__PURE__*/React.createElement(Box, {
|
|
49
|
+
flexDirection: "column",
|
|
50
|
+
alignItems: "center"
|
|
51
|
+
}, /*#__PURE__*/React.createElement(Box, {
|
|
52
|
+
marginY: 2,
|
|
53
|
+
flexDirection: "column",
|
|
54
|
+
justifyContent: "center"
|
|
55
|
+
}, /*#__PURE__*/React.createElement(Gradient, {
|
|
56
|
+
name: "rainbow"
|
|
57
|
+
}, /*#__PURE__*/React.createElement(Text, null, "TYPING-GAME-CLI"))), /*#__PURE__*/React.createElement(Box, {
|
|
58
|
+
rowGap: 1,
|
|
59
|
+
flexDirection: "column",
|
|
60
|
+
justifyContent: "center",
|
|
61
|
+
alignItems: "center"
|
|
62
|
+
}, /*#__PURE__*/React.createElement(Text, null, "Typer-robot challenges you: who will type a text faster."), /*#__PURE__*/React.createElement(Text, null, "Press", ' ', /*#__PURE__*/React.createElement(Text, {
|
|
63
|
+
bold: true,
|
|
64
|
+
color: optionKeyColor
|
|
65
|
+
}, "y"), ' ', "if you want to accept a challenge and start a round."))) : /*#__PURE__*/React.createElement(Box, {
|
|
66
|
+
flexDirection: "column",
|
|
67
|
+
alignItems: "center"
|
|
68
|
+
}, isFinished(snap.status) ? /*#__PURE__*/React.createElement(Box, {
|
|
69
|
+
flexDirection: "column"
|
|
70
|
+
}, /*#__PURE__*/React.createElement(Box, {
|
|
71
|
+
paddingY: 1
|
|
72
|
+
}, /*#__PURE__*/React.createElement(Text, null, "Press", ' ', /*#__PURE__*/React.createElement(Text, {
|
|
73
|
+
bold: true,
|
|
74
|
+
color: optionKeyColor
|
|
75
|
+
}, "y"), ' ', "to start a new round.")), /*#__PURE__*/React.createElement(Box, {
|
|
76
|
+
flexDirection: "column",
|
|
77
|
+
alignItems: "center",
|
|
78
|
+
justifyContent: "center"
|
|
79
|
+
}, /*#__PURE__*/React.createElement(Box, null, /*#__PURE__*/React.createElement(Alert, {
|
|
80
|
+
variant: getStatusVariant(snap.status)
|
|
81
|
+
}, getMessage(snap.status))))) : /*#__PURE__*/React.createElement(Box, null, /*#__PURE__*/React.createElement(Box, {
|
|
82
|
+
borderStyle: "single",
|
|
83
|
+
alignItems: "center",
|
|
84
|
+
justifyContent: "center"
|
|
85
|
+
}, /*#__PURE__*/React.createElement(Spinner, null), /*#__PURE__*/React.createElement(Text, null, " Running"))), /*#__PURE__*/React.createElement(Box, {
|
|
86
|
+
paddingBottom: 1
|
|
87
|
+
}, /*#__PURE__*/React.createElement(Text, null, snap.source)), /*#__PURE__*/React.createElement(Box, {
|
|
88
|
+
alignItems: "center",
|
|
89
|
+
justifyContent: "center"
|
|
90
|
+
}, /*#__PURE__*/React.createElement(Box, {
|
|
91
|
+
borderStyle: "single",
|
|
92
|
+
width: stdout.columns / 2.15,
|
|
93
|
+
borderColor: getBorderColor(snap.status)
|
|
94
|
+
}, isFinished(snap.status) ? /*#__PURE__*/React.createElement(Text, null, snap.userText) : /*#__PURE__*/React.createElement(TextInput, {
|
|
95
|
+
value: snap.userText,
|
|
96
|
+
onChange: value => {
|
|
97
|
+
state.userText = value;
|
|
98
|
+
}
|
|
99
|
+
})), /*#__PURE__*/React.createElement(Box, {
|
|
100
|
+
borderStyle: "single",
|
|
101
|
+
width: stdout.columns / 2.15,
|
|
102
|
+
borderColor: getRobotBorderColor(snap.status)
|
|
103
|
+
}, /*#__PURE__*/React.createElement(Text, null, snap.robotText))));
|
|
104
|
+
}
|
package/dist/cli.js
ADDED
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import React from 'react';
|
|
3
|
+
import { render } from 'ink';
|
|
4
|
+
import meow from 'meow';
|
|
5
|
+
import pick from 'just-pick';
|
|
6
|
+
import compose from 'just-compose';
|
|
7
|
+
import filter from 'just-filter-object';
|
|
8
|
+
import App from './app.js';
|
|
9
|
+
const cli = meow(`
|
|
10
|
+
Usage
|
|
11
|
+
$ typing-game-cli
|
|
12
|
+
|
|
13
|
+
Options
|
|
14
|
+
--fast Start a round with a robot having high typing speed.
|
|
15
|
+
--medium Start a round with a robot having medium typing speed.
|
|
16
|
+
--low Start a round with a robot having low typing speed.
|
|
17
|
+
|
|
18
|
+
Examples
|
|
19
|
+
$ typing-game-cli
|
|
20
|
+
$ typing-game-cli --fast
|
|
21
|
+
$ typing-game-cli --medium
|
|
22
|
+
$ typing-game-cli --low
|
|
23
|
+
`, {
|
|
24
|
+
importMeta: import.meta,
|
|
25
|
+
flags: {
|
|
26
|
+
fast: {
|
|
27
|
+
type: 'boolean',
|
|
28
|
+
shortFlag: 'f'
|
|
29
|
+
},
|
|
30
|
+
medium: {
|
|
31
|
+
type: 'boolean',
|
|
32
|
+
shortFlag: 'm'
|
|
33
|
+
},
|
|
34
|
+
low: {
|
|
35
|
+
type: 'boolean',
|
|
36
|
+
shortFlag: 'l'
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
});
|
|
40
|
+
const robotLevel = compose(flags => pick(flags, ['fast', 'medium', 'low']), flags => filter(flags, (_, value) => value), flags => Object.keys(flags)[0] || 'medium')(cli.flags);
|
|
41
|
+
render( /*#__PURE__*/React.createElement(App, {
|
|
42
|
+
robotLevel: robotLevel
|
|
43
|
+
}));
|
package/dist/helpers.js
ADDED
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import fs from 'node:fs';
|
|
2
|
+
import path from 'node:path';
|
|
3
|
+
import { fileURLToPath } from 'node:url';
|
|
4
|
+
import { fdir as Fdir } from 'fdir';
|
|
5
|
+
import random from 'just-random';
|
|
6
|
+
const __filename = fileURLToPath(import.meta.url);
|
|
7
|
+
const __dirname = path.dirname(__filename);
|
|
8
|
+
const won = 'WON';
|
|
9
|
+
const lost = 'LOST';
|
|
10
|
+
const sourcesPath = path.join(__dirname, 'sentences');
|
|
11
|
+
const isWon = value => value === won;
|
|
12
|
+
const isLost = value => value === lost;
|
|
13
|
+
const files = new Fdir().filter((path, _isDirectory) => path.endsWith('.json')).withFullPaths().crawl(sourcesPath).sync();
|
|
14
|
+
const getDefaultSuite = () => {
|
|
15
|
+
return JSON.parse(fs.readFileSync(random(files), 'utf8'));
|
|
16
|
+
};
|
|
17
|
+
const getStatusVariant = status => {
|
|
18
|
+
if (isWon(status)) return 'success';
|
|
19
|
+
if (isLost(status)) return 'error';
|
|
20
|
+
return '';
|
|
21
|
+
};
|
|
22
|
+
const getMessage = status => {
|
|
23
|
+
if (isWon(status)) return 'You won!';
|
|
24
|
+
if (isLost(status)) return 'Robot won!';
|
|
25
|
+
return '';
|
|
26
|
+
};
|
|
27
|
+
const getBorderColor = status => {
|
|
28
|
+
if (isWon(status)) return 'green';
|
|
29
|
+
return '';
|
|
30
|
+
};
|
|
31
|
+
const getRobotBorderColor = status => {
|
|
32
|
+
if (isLost(status)) return 'green';
|
|
33
|
+
return '';
|
|
34
|
+
};
|
|
35
|
+
const getIntervalMs = level => {
|
|
36
|
+
return {
|
|
37
|
+
fast: 270,
|
|
38
|
+
medium: 380,
|
|
39
|
+
low: 1600
|
|
40
|
+
}[level];
|
|
41
|
+
};
|
|
42
|
+
const isFinished = status => [won, lost].includes(status);
|
|
43
|
+
export { getDefaultSuite, getStatusVariant, getMessage, getBorderColor, getRobotBorderColor, getIntervalMs, isFinished };
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
{
|
|
2
|
+
"sentences": [
|
|
3
|
+
"Had it affected his mind? His reply to my question seemed to me then evidence that it had; perhaps I should think differently about it now.",
|
|
4
|
+
"In an open spot in my garden I planted a climbing vine. When it was barely above the surface I set a stake into the soil a yard away.",
|
|
5
|
+
"The vine at once made for it, but as it was about to reach it after several days I removed it a few feet. The vine at once altered its course, making an acute angle, and again made for the stake.",
|
|
6
|
+
"We were speaking, not of plants, but of machines. They may be composed partly of wood - wood that has no longer vitality - or wholly of metal.",
|
|
7
|
+
"When soldiers form lines, or hollow squares, you call it reason. When wild geese in flight take the form of a letter V you say instinct.",
|
|
8
|
+
"I read it thirty years ago. He may have altered it afterward, for anything I know, but in all that time I have been unable to think of a single word that could profitably be changed or added or removed.",
|
|
9
|
+
"At each recurrence it broadened in meaning and deepened in suggestion. Why, here (I thought) is something upon which to found a philosophy.",
|
|
10
|
+
"I was drenched with rain, but felt no discomfort. Unable in my excitement to find the doorbell I instinctively tried the knob.",
|
|
11
|
+
"What I saw took all philosophical speculation out of me in short order. Moxon sat facing me at the farther side of a small table upon which a single candle made all the light that was in the room.",
|
|
12
|
+
"Opposite him, his back toward me, sat another person. On the table between the two was a chess-board; the men were playing.",
|
|
13
|
+
"His face was ghastly white, and his eyes glittered like diamonds. Of his antagonist I had only a back view, but that was sufficient; I should not have cared to see his face.",
|
|
14
|
+
"With a scarcely conscious rebellion against the indelicacy of the act I remained. The play was rapid.",
|
|
15
|
+
"There was something unearthly about it all, and I caught myself shuddering. But I was wet and cold.",
|
|
16
|
+
"Two or three times after moving a piece the stranger slightly inclined his head, and each time I observed that Moxon shifted his king. All at once the thought came to me that the man was dumb.",
|
|
17
|
+
"Nor was that all, for a moment later it struck the table sharply with its clenched hand. At that gesture Moxon seemed even more startled than I: he pushed his chair a little backward, as in alarm.",
|
|
18
|
+
"The automaton sat motionless. The wind had now gone down, but I heard, at lessening intervals and progressively louder, the rumble and roll of thunder.",
|
|
19
|
+
"This I observed, then all was blackness and silence. Three days later I recovered consciousness in a hospital.",
|
|
20
|
+
"Nobody knows how you came to be there. You may have to do a little explaining.",
|
|
21
|
+
"The origin of the fire is a bit mysterious, too. My own notion is that the house was struck by lightning."
|
|
22
|
+
],
|
|
23
|
+
"tags": ["ambrose-bierce", "moxon-s-master"]
|
|
24
|
+
}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
{
|
|
2
|
+
"sentences": [
|
|
3
|
+
"With what pure delight we inhaled its fragrances of spruce and pine! How we stared with something like awe at its clumps of laurel!",
|
|
4
|
+
"He was lying flat upon his stomach and was killed by being struck in the side by a nearly spent cannon-shot, that came rolling in among us. The shot remained in him until removed.",
|
|
5
|
+
"The long logs that it was our pride to cut and carry! The accuracy with which we laid them one upon another, hewn to the line and bullet-proof!",
|
|
6
|
+
"There can be no doubt that the wealthy sportsmen who have made a preserve of the Cheat Mountain region will find plenty of game if it has not died since 1861. We left it there.",
|
|
7
|
+
"The frost had begun already to whiten their deranged clothing. We were as patriotic as ever, but we did not wish to be that way.",
|
|
8
|
+
"They appeared also to have thrown off some of their clothing, which lay near by, in disorder. Their expression, too, had an added blankness - they had no faces.",
|
|
9
|
+
"As soon as the head of our straggling column had reached the spot a desultory firing had begun. One might have thought the living paid honors to the dead.",
|
|
10
|
+
"No; the firing was a military execution; the condemned, a herd of galloping swine. They had eaten our fallen, but - touching magnanimity!"
|
|
11
|
+
],
|
|
12
|
+
"tags": ["ambrose-bierce", "on-a-mountain"]
|
|
13
|
+
}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
{
|
|
2
|
+
"sentences": [
|
|
3
|
+
"Morgan, when he was driven from Cumberland Gap to the Ohio river by General Kirby Smith. At the time of its destruction, it had for four or five years been vacant.",
|
|
4
|
+
"Attributing this to the continuous uproar of the thunder they pushed at one of the doors, which yielded. They entered without further ceremony and closed the door.",
|
|
5
|
+
"My notion was to ascertain by stepping again into the storm whether I had been deprived of sight and hearing. I turned the doorknob and pulled open the door.",
|
|
6
|
+
"They were of different ages, or rather sizes, from infancy up, and of both sexes. All were prostrate on the floor, excepting one, apparently a young woman, who sat up, her back supported by an angle of the wall.",
|
|
7
|
+
"A babe was clasped in the arms of another and older woman. A half- grown lad lay face downward across the legs of a full-bearded man.",
|
|
8
|
+
"One or two were nearly naked, and the hand of a young girl held the fragment of a gown which she had torn open at the breast. The bodies were in various stages of decay, all greatly shrunken in face and figure.",
|
|
9
|
+
"Equidistant from one another and from the top and bottom, three strong bolts protruded from the beveled edge. I turned the knob and they were retracted flush with the edge; released it, and they shot out.",
|
|
10
|
+
"It was a spring lock. On the inside there was no knob, nor any kind of projection - a smooth surface of iron.",
|
|
11
|
+
"A strong disagreeable odor came through the doorway, completely overpowering me. My senses reeled; I felt myself falling, and in clutching at the edge of the door for support pushed it shut with a sharp click!"
|
|
12
|
+
],
|
|
13
|
+
"tags": ["ambrose-bierce", "the-spook-house"]
|
|
14
|
+
}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
{
|
|
2
|
+
"sentences": [
|
|
3
|
+
"For some three years before the date mentioned above, it was occupied by the family of Charles May, from one of whose ancestors the creek near which it stands took its name.",
|
|
4
|
+
"It was observed that his clothing was wet in spots, as if (so the prosecution afterward pointed out) he had been removing blood-stains from it. His manner was strange, his look wild.",
|
|
5
|
+
"He complained of illness, and going to his room took to his bed. May senior did not return.",
|
|
6
|
+
"On Wednesday all was changed. From the town of Nolan, eight miles away, came a story which put a quite different light on the matter.",
|
|
7
|
+
"He was without hat or coat. He did not look at them, nor return their greeting, a circumstance which did not surprise, for he was evidently seriously hurt.",
|
|
8
|
+
"Counsel for the defense appears not to have demurred and the case was tried on its merits. The prosecution was spiritless and perfunctory; the defense easily established - with regard to the deceased - an alibi.",
|
|
9
|
+
"Shortly afterward his mother and sisters removed to St. Louis.",
|
|
10
|
+
"The skull had been cut through by the blow. The body was that of Charles May."
|
|
11
|
+
],
|
|
12
|
+
"tags": ["ambrose-bierce", "the-thing-at-nolan"]
|
|
13
|
+
}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
{
|
|
2
|
+
"sentences": [
|
|
3
|
+
"I was spending the month of March 1892 at Mentone, in the Riviera. At this retired spot one has all the advantages, privately, which are to be had publicly at Monte Carlo and Nice, a few miles farther along.",
|
|
4
|
+
"As a rule, I mean, the rich do not come there. Now and then a rich man comes, and I presently got acquainted with one of these.",
|
|
5
|
+
"Partially to disguise him I will call him Smith. One day, in the Hotel des Anglais, at the second breakfast, he exclaimed: 'Quick!",
|
|
6
|
+
"Cast your eye on the man going out at the door. Take in every detail of him.",
|
|
7
|
+
"He is an old, retired, and very rich silk manufacturer from Lyons, they say, and I guess he is alone in the world, for he always looks sad and dreamy, and doesn't talk with anybody. His name is Theophile Magnan.",
|
|
8
|
+
"It has been a secret for many years - a secret between me and three others; but I am going to break the seal now. Are you comfortable?",
|
|
9
|
+
"He wasn't any greater than we were, then. He hadn't any fame, even in his own village; and he was so poor that he hadn't anything to feed us on but turnips, and even the turnips failed us sometimes.",
|
|
10
|
+
"We four became fast friends, doting friends, inseparables. We painted away together with all our might, piling up stock, piling up stock, but very seldom getting rid of any of it.",
|
|
11
|
+
"We had lovely times together; but, O my soul! how we were pinched now and then!",
|
|
12
|
+
"Everybody has struck - there's a league formed against us. I've been all around the village and it's just as I tell you.",
|
|
13
|
+
"Every face was blank with dismay. We realised that our circumstances were desperate, now."
|
|
14
|
+
],
|
|
15
|
+
"tags": ["marktwain", "ishelivingorishedead"]
|
|
16
|
+
}
|
package/package.json
ADDED
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "typing-game-cli",
|
|
3
|
+
"description": "Command line game to practice your typing speed",
|
|
4
|
+
"version": "1.0.0",
|
|
5
|
+
"homepage": "https://github.com/akgondber/typing-game-cli",
|
|
6
|
+
"repository": "akgondber/typing-game-cli",
|
|
7
|
+
"license": "MIT",
|
|
8
|
+
"bin": {
|
|
9
|
+
"typing-game-cli": "dist/cli.js",
|
|
10
|
+
"typing-game": "dist/cli.js",
|
|
11
|
+
"tgc": "dist/cli.js",
|
|
12
|
+
"tpngm": "dist/cli.js"
|
|
13
|
+
},
|
|
14
|
+
"type": "module",
|
|
15
|
+
"engines": {
|
|
16
|
+
"node": ">=16"
|
|
17
|
+
},
|
|
18
|
+
"scripts": {
|
|
19
|
+
"build": "babel --out-dir=dist source --copy-files",
|
|
20
|
+
"dev": "babel --out-dir=dist --watch source",
|
|
21
|
+
"test": "prettier --check . && xo && ava",
|
|
22
|
+
"lint": "xo",
|
|
23
|
+
"go": "node dist/cli.js",
|
|
24
|
+
"prettify": "prettier . --write",
|
|
25
|
+
"xofix": "xo --fix"
|
|
26
|
+
},
|
|
27
|
+
"files": [
|
|
28
|
+
"dist"
|
|
29
|
+
],
|
|
30
|
+
"dependencies": {
|
|
31
|
+
"@inkjs/ui": "^1.0.0",
|
|
32
|
+
"fdir": "^6.1.1",
|
|
33
|
+
"ink": "^4.2.0",
|
|
34
|
+
"ink-gradient": "^3.0.0",
|
|
35
|
+
"ink-text-input-2": "^1.0.0",
|
|
36
|
+
"just-compose": "^2.3.0",
|
|
37
|
+
"just-curry-it": "^5.3.0",
|
|
38
|
+
"just-filter-object": "^3.2.0",
|
|
39
|
+
"just-flip": "^2.2.0",
|
|
40
|
+
"just-partial-it": "^3.4.0",
|
|
41
|
+
"just-pick": "^4.2.0",
|
|
42
|
+
"just-random": "^3.2.0",
|
|
43
|
+
"meow": "^12.0.1",
|
|
44
|
+
"react": "^18.2.0",
|
|
45
|
+
"valtio": "^1.13.0"
|
|
46
|
+
},
|
|
47
|
+
"devDependencies": {
|
|
48
|
+
"@babel/cli": "^7.22.5",
|
|
49
|
+
"@babel/preset-react": "^7.22.5",
|
|
50
|
+
"ava": "^6.1.1",
|
|
51
|
+
"chalk": "^5.2.0",
|
|
52
|
+
"eslint-config-xo-react": "^0.27.0",
|
|
53
|
+
"eslint-plugin-react": "^7.32.2",
|
|
54
|
+
"eslint-plugin-react-hooks": "^4.6.0",
|
|
55
|
+
"import-jsx": "^5.0.0",
|
|
56
|
+
"ink-testing-library": "^3.0.0",
|
|
57
|
+
"prettier": "^2.8.8",
|
|
58
|
+
"xo": "^0.54.2"
|
|
59
|
+
},
|
|
60
|
+
"ava": {
|
|
61
|
+
"environmentVariables": {
|
|
62
|
+
"NODE_NO_WARNINGS": "1"
|
|
63
|
+
},
|
|
64
|
+
"nodeArguments": [
|
|
65
|
+
"--loader=import-jsx"
|
|
66
|
+
]
|
|
67
|
+
},
|
|
68
|
+
"xo": {
|
|
69
|
+
"extends": "xo-react",
|
|
70
|
+
"prettier": true,
|
|
71
|
+
"rules": {
|
|
72
|
+
"react/prop-types": "off"
|
|
73
|
+
}
|
|
74
|
+
},
|
|
75
|
+
"prettier": {
|
|
76
|
+
"useTabs": true,
|
|
77
|
+
"semi": true,
|
|
78
|
+
"singleQuote": true,
|
|
79
|
+
"quoteProps": "as-needed",
|
|
80
|
+
"bracketSpacing": false,
|
|
81
|
+
"arrowParens": "avoid",
|
|
82
|
+
"trailingComma": "all"
|
|
83
|
+
},
|
|
84
|
+
"babel": {
|
|
85
|
+
"presets": [
|
|
86
|
+
"@babel/preset-react"
|
|
87
|
+
]
|
|
88
|
+
},
|
|
89
|
+
"keywords": [
|
|
90
|
+
"challenge",
|
|
91
|
+
"cli",
|
|
92
|
+
"fun",
|
|
93
|
+
"game",
|
|
94
|
+
"keyboard",
|
|
95
|
+
"productivity",
|
|
96
|
+
"robot",
|
|
97
|
+
"speed",
|
|
98
|
+
"typer",
|
|
99
|
+
"typing"
|
|
100
|
+
]
|
|
101
|
+
}
|
package/readme.md
ADDED
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
# typing-game-cli [![NPM version][npm-image]][npm-url]
|
|
2
|
+
|
|
3
|
+
> Command line game to practice your typing speed.
|
|
4
|
+
|
|
5
|
+
## Install
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
$ npm install --global typing-game-cli
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
## CLI
|
|
12
|
+
|
|
13
|
+
```
|
|
14
|
+
$ typing-game-cli --help
|
|
15
|
+
|
|
16
|
+
Usage
|
|
17
|
+
$ typing-game-cli
|
|
18
|
+
|
|
19
|
+
Options
|
|
20
|
+
--fast Start a round with a robot having high typing speed.
|
|
21
|
+
--medium Start a round with a robot having medium typing speed.
|
|
22
|
+
--low Start a round with a robot having low typing speed.
|
|
23
|
+
|
|
24
|
+
Examples
|
|
25
|
+
$ typing-game-cli
|
|
26
|
+
$ typing-game-cli --fast
|
|
27
|
+
$ typing-game-cli --medium
|
|
28
|
+
$ typing-game-cli --low
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
## Demo
|
|
32
|
+
|
|
33
|
+

|
|
34
|
+
|
|
35
|
+
## Screenshots
|
|
36
|
+
|
|
37
|
+

|
|
38
|
+
|
|
39
|
+
## License
|
|
40
|
+
|
|
41
|
+
MIT © [Rushan Alyautdinov](https://github.com/akgondber)
|
|
42
|
+
|
|
43
|
+
[npm-image]: https://img.shields.io/npm/v/typing-game-cli.svg?style=flat
|
|
44
|
+
[npm-url]: https://npmjs.org/package/typing-game-cli
|