typefaster-cli 0.1.0__py3-none-any.whl

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.
Files changed (61) hide show
  1. typefaster/__init__.py +3 -0
  2. typefaster/__main__.py +8 -0
  3. typefaster/assets/__init__.py +1 -0
  4. typefaster/assets/quotes.json +2642 -0
  5. typefaster/cli.py +196 -0
  6. typefaster/domain/__init__.py +1 -0
  7. typefaster/domain/anti_cheat.py +46 -0
  8. typefaster/domain/calculators.py +47 -0
  9. typefaster/domain/errors.py +19 -0
  10. typefaster/domain/ghost.py +61 -0
  11. typefaster/domain/models.py +186 -0
  12. typefaster/domain/typing_engine.py +150 -0
  13. typefaster/infra/__init__.py +1 -0
  14. typefaster/infra/clock.py +36 -0
  15. typefaster/infra/config.py +41 -0
  16. typefaster/infra/db.py +31 -0
  17. typefaster/infra/migrations.py +106 -0
  18. typefaster/infra/paths.py +35 -0
  19. typefaster/infra/quote_loader.py +53 -0
  20. typefaster/infra/replay_store.py +16 -0
  21. typefaster/infra/repository.py +63 -0
  22. typefaster/infra/sqlite_repository.py +360 -0
  23. typefaster/net/__init__.py +1 -0
  24. typefaster/net/api.py +103 -0
  25. typefaster/net/commands.py +188 -0
  26. typefaster/net/token_store.py +46 -0
  27. typefaster/services/__init__.py +1 -0
  28. typefaster/services/container.py +42 -0
  29. typefaster/services/daily_service.py +23 -0
  30. typefaster/services/ghost_service.py +50 -0
  31. typefaster/services/profile_service.py +18 -0
  32. typefaster/services/race_service.py +152 -0
  33. typefaster/services/stats_service.py +52 -0
  34. typefaster/ui/__init__.py +1 -0
  35. typefaster/ui/app.py +93 -0
  36. typefaster/ui/online_app.py +24 -0
  37. typefaster/ui/screens/__init__.py +1 -0
  38. typefaster/ui/screens/_base.py +30 -0
  39. typefaster/ui/screens/daily.py +64 -0
  40. typefaster/ui/screens/help.py +26 -0
  41. typefaster/ui/screens/history.py +34 -0
  42. typefaster/ui/screens/leaderboard.py +52 -0
  43. typefaster/ui/screens/main_menu.py +96 -0
  44. typefaster/ui/screens/online_race.py +241 -0
  45. typefaster/ui/screens/practice.py +57 -0
  46. typefaster/ui/screens/profile.py +28 -0
  47. typefaster/ui/screens/race.py +238 -0
  48. typefaster/ui/screens/results.py +89 -0
  49. typefaster/ui/screens/settings.py +81 -0
  50. typefaster/ui/screens/stats.py +65 -0
  51. typefaster/ui/theme.py +82 -0
  52. typefaster/ui/widgets/__init__.py +1 -0
  53. typefaster/ui/widgets/bigtext.py +39 -0
  54. typefaster/ui/widgets/live_stats.py +19 -0
  55. typefaster/ui/widgets/progress_bars.py +44 -0
  56. typefaster/ui/widgets/typing_field.py +28 -0
  57. typefaster_cli-0.1.0.dist-info/METADATA +168 -0
  58. typefaster_cli-0.1.0.dist-info/RECORD +61 -0
  59. typefaster_cli-0.1.0.dist-info/WHEEL +4 -0
  60. typefaster_cli-0.1.0.dist-info/entry_points.txt +2 -0
  61. typefaster_cli-0.1.0.dist-info/licenses/LICENSE +21 -0
@@ -0,0 +1,28 @@
1
+ """Renders the target text with per-character correctness coloring + caret."""
2
+
3
+ from __future__ import annotations
4
+
5
+ from rich.text import Text
6
+ from textual.widgets import Static
7
+
8
+ from .. import theme
9
+
10
+
11
+ class TypingField(Static):
12
+ """Displays the quote, coloring each character by typed state."""
13
+
14
+ def show(self, target: str, states: list[bool | None], cursor: int) -> None:
15
+ text = Text()
16
+ for i, ch in enumerate(target):
17
+ if i == cursor:
18
+ text.append(ch, style=theme.CARET)
19
+ elif i < cursor:
20
+ state = states[i]
21
+ if state is True:
22
+ text.append(ch, style=theme.CORRECT)
23
+ else:
24
+ # Wrong char: keep the *target* glyph visible, mark it red.
25
+ text.append(ch, style=theme.INCORRECT)
26
+ else:
27
+ text.append(ch, style=theme.PENDING)
28
+ self.update(text)
@@ -0,0 +1,168 @@
1
+ Metadata-Version: 2.4
2
+ Name: typefaster-cli
3
+ Version: 0.1.0
4
+ Summary: A terminal-first typing game inspired by MonkeyType and TypeRacer.
5
+ Project-URL: Homepage, https://github.com/Anoshor/typefaster-cli
6
+ Project-URL: Repository, https://github.com/Anoshor/typefaster-cli
7
+ Author: Anoshor Paul
8
+ License: MIT
9
+ License-File: LICENSE
10
+ Keywords: cli,game,monkeytype,terminal,tui,typeracer,typing,wpm
11
+ Classifier: Environment :: Console
12
+ Classifier: Intended Audience :: End Users/Desktop
13
+ Classifier: Programming Language :: Python :: 3.11
14
+ Classifier: Programming Language :: Python :: 3.12
15
+ Classifier: Topic :: Games/Entertainment
16
+ Requires-Python: >=3.11
17
+ Requires-Dist: httpx>=0.27
18
+ Requires-Dist: platformdirs>=4.2
19
+ Requires-Dist: rich>=13.7
20
+ Requires-Dist: textual>=0.60
21
+ Requires-Dist: typer>=0.12
22
+ Requires-Dist: websockets>=12.0
23
+ Provides-Extra: dev
24
+ Requires-Dist: black>=24.0; extra == 'dev'
25
+ Requires-Dist: mypy>=1.10; extra == 'dev'
26
+ Requires-Dist: pytest-asyncio>=0.23; extra == 'dev'
27
+ Requires-Dist: pytest-cov>=5.0; extra == 'dev'
28
+ Requires-Dist: pytest>=8.0; extra == 'dev'
29
+ Requires-Dist: ruff>=0.5; extra == 'dev'
30
+ Requires-Dist: textual-dev>=1.5; extra == 'dev'
31
+ Description-Content-Type: text/markdown
32
+
33
+ # ⌨ TYPEFASTER-CLI
34
+
35
+ [![CI](https://github.com/Anoshor/typefaster-cli/actions/workflows/ci.yml/badge.svg)](https://github.com/Anoshor/typefaster-cli/actions/workflows/ci.yml)
36
+ [![Python](https://img.shields.io/badge/python-3.11%2B-blue)](https://www.python.org/downloads/)
37
+ [![License: MIT](https://img.shields.io/badge/license-MIT-green)](LICENSE)
38
+
39
+ A **terminal-first** typing game inspired by MonkeyType and TypeRacer.
40
+
41
+ > Not a web app. Not a browser game. Not a desktop GUI.
42
+ > A polished **Python terminal application** that works offline first, then scales to internet multiplayer.
43
+
44
+ ```bash
45
+ typefaster
46
+ ```
47
+
48
+ …and you're racing within seconds. No login, no server, no Docker, no internet required.
49
+
50
+ ---
51
+
52
+ ## Status
53
+
54
+ | Phase | Scope | State |
55
+ |-------|-------|-------|
56
+ | **Phase 1** | Offline experience: races, ghosts, profile, stats, history, daily challenge | ✅ **Implemented & tested** |
57
+ | **Phase 2** | Online multiplayer: FastAPI + Redis + WebSockets, auth, lobbies, leaderboards, anti-cheat, Docker | ✅ **Implemented & tested** |
58
+
59
+ Both phases are implemented. Offline play needs only `pip install`; online play
60
+ adds a Dockerized server (see [Online play](#online-play-phase-2)).
61
+
62
+ ---
63
+
64
+ ## What Phase 1 delivers
65
+
66
+ - **Instant offline races** — random quote, live WPM / accuracy / progress / timer.
67
+ - **30 / 60 / 120 second** race modes.
68
+ - **Ghost races** against your `personal-best`, `last`, or a `random` historical run, animated live.
69
+ - **Local profile & stats** in SQLite — races played/won, best/avg WPM, best/avg accuracy, total chars, total time, full history.
70
+ - **Daily challenge** — same quote for everyone each day, with a local daily leaderboard.
71
+ - **Polished TUI** built on **Textual** + **Rich**, keyboard-only, resize-aware.
72
+
73
+ ## Planned CLI
74
+
75
+ ```bash
76
+ typefaster # launch straight into the game
77
+ typefaster race --time 60 --ghost personal-best
78
+ typefaster race --ghost last
79
+ typefaster race --ghost random
80
+ typefaster daily
81
+ typefaster profile
82
+ typefaster stats
83
+ typefaster history
84
+ ```
85
+
86
+ ---
87
+
88
+ ## Tech stack
89
+
90
+ **Client (Phase 1):** Python 3.11+, Typer, Rich, Textual, SQLite (stdlib), platformdirs.
91
+ **Server (Phase 2):** FastAPI, asyncio, WebSockets, Pydantic, Redis, Docker Compose.
92
+
93
+ ## Repository layout
94
+
95
+ ```
96
+ typefaster-cli/
97
+ ├── client/typefaster/ # CLI app: domain · services · infra · ui · net · assets
98
+ ├── server/app/ # FastAPI server: routers · ws · repositories · security
99
+ ├── shared/ # shared schemas, WS protocol, scoring, anti-cheat
100
+ ├── infra/ # redis.conf · nginx.conf (TLS + WS proxy)
101
+ ├── docs/ # architecture, schemas, protocol, deployment, roadmap
102
+ ├── tests/ # client unit · integration · UI smoke
103
+ ├── scripts/ # quote dataset tooling
104
+ ├── docker-compose.yml # redis + server (+ nginx via --profile proxy)
105
+ ├── pyproject.toml · Makefile · README.md
106
+ ```
107
+
108
+ See [`docs/architecture.md`](docs/architecture.md) for the full design.
109
+
110
+ ## Online play (Phase 2)
111
+
112
+ Run the server stack (Redis + FastAPI + WebSockets) with Docker:
113
+
114
+ ```bash
115
+ cp .env.example .env # set TYPEFASTER_JWT_SECRET
116
+ make up # redis + server on :8000 (make up-proxy adds nginx TLS)
117
+ ```
118
+
119
+ Then, from the client:
120
+
121
+ ```bash
122
+ typefaster register alice # create an account
123
+ typefaster login alice
124
+ typefaster lobby create --name "Friday Sprint" --time 60
125
+ typefaster lobby join ABC123 # join a friend's private lobby
126
+ typefaster lobby list # browse public lobbies
127
+ typefaster leaderboard global # global | daily | weekly
128
+ typefaster logout
129
+ ```
130
+
131
+ The server is **authoritative**: it controls race start/finish, re-scores every
132
+ result, and runs anti-cheat before writing leaderboards. See the docs:
133
+
134
+ - [API specification](docs/api-spec.md)
135
+ - [WebSocket protocol](docs/websocket-protocol.md)
136
+ - [Redis schema](docs/redis-schema.md)
137
+ - [Deployment guide (single Linux VM)](docs/deployment.md)
138
+
139
+ The client points at `http://localhost:8000` by default; set `server_url` in
140
+ `~/.config/typefaster/auth.json` to target a deployed server.
141
+
142
+ ## Development
143
+
144
+ ```bash
145
+ make install # editable install + dev deps
146
+ make play # launch the game
147
+ make test # pytest
148
+ make lint # ruff
149
+ make typecheck # mypy
150
+ make format # black + ruff --fix
151
+ make check # lint + typecheck + test (CI parity)
152
+ ```
153
+
154
+ > **Note on the monorepo layout:** the importable package lives at
155
+ > `client/typefaster`. A normal install (`pip install .`, used by Docker and end
156
+ > users) places it on the path automatically. For local development the
157
+ > `Makefile` exports `PYTHONPATH=client`, so `make play` / `make test` always
158
+ > work. If you invoke tools directly, prefix with `PYTHONPATH=client` (e.g.
159
+ > `PYTHONPATH=client python -m typefaster`).
160
+
161
+ ## Design preferences locked for Phase 1
162
+
163
+ - **Quotes:** curated public-domain set, tagged `short` / `medium` / `long` for difficulty buckets and 30/60/120s fit.
164
+ - **Backspace:** allowed (MonkeyType-style) — corrections permitted, original errors still count toward accuracy; exposed as a Settings toggle.
165
+
166
+ ## License
167
+
168
+ MIT.
@@ -0,0 +1,61 @@
1
+ typefaster/__init__.py,sha256=uqCP1MzJp9nieFYiz6ANwx0ar5DukClA7WRewqrePRQ,78
2
+ typefaster/__main__.py,sha256=2dxSnCI2wKDhIgdMFPwlZkdbJ-GQwy6uZApfeoyFAkA,135
3
+ typefaster/cli.py,sha256=PGkhtsID-GDSPGgSY-sFsLj4WGYXO6EsP1Nwx9-feig,6500
4
+ typefaster/assets/__init__.py,sha256=uDRfxDegVWAPzgSUlv-yy_ILP5UTMVIa9lNTh4moyU4,43
5
+ typefaster/assets/quotes.json,sha256=ooE992FtEyzB6-cJ6BEPx_Hfzs5iSekEb8wPvqvOHP4,73591
6
+ typefaster/domain/__init__.py,sha256=5y9Qy9eMH_c75Gq_um7E-6qVPB1a2r0flS-yIVaCqEk,73
7
+ typefaster/domain/anti_cheat.py,sha256=zwB5-ZPANMX63zWxwDn8d9EPtW3kf6PGQ1g-35fAQeM,1429
8
+ typefaster/domain/calculators.py,sha256=Igj1ivDyM67Uf-7hu6hm9H2BCOAvbgaqQ_a0dU6H6t8,1438
9
+ typefaster/domain/errors.py,sha256=AP-wzNosxDpxAwtQ8cXfhl32xVGodm3ablp2wawJeak,493
10
+ typefaster/domain/ghost.py,sha256=ilLKCJ24nJs3NQvpetuQwJxJaeq0t6QOrGYYEydbnvQ,2072
11
+ typefaster/domain/models.py,sha256=3WfhzH2lrtWdqxuieBOX81WnprAaNuYQepS-VLMGmoE,4418
12
+ typefaster/domain/typing_engine.py,sha256=D27fCvog5k7i9sygVWvchNojA4cS7Rl5noU8O5AoTAo,5559
13
+ typefaster/infra/__init__.py,sha256=i_oY7TZxRHpRRp61_m-qQWsg8qk0CTVHxHT9UGV1do4,76
14
+ typefaster/infra/clock.py,sha256=6DZWBtaTueN0cisnFEXs9bwLuNWb1NOY8e8LiC7w8Nc,775
15
+ typefaster/infra/config.py,sha256=Tz1waDd6QuvVYB0NE0rkbtS5a1mowsoLpx9KlFt4kuM,1229
16
+ typefaster/infra/db.py,sha256=5S2v-o__L9EYaFAyg6WmS7eRL2qszwZIv_S23IHB4AM,942
17
+ typefaster/infra/migrations.py,sha256=kb6VNWw42JNck3yyF_9pQUcIVzn7EaFbJsORqZBNg2g,3995
18
+ typefaster/infra/paths.py,sha256=aH8IeRj34_-_mG4C5450bE_mvThgy3B6Xb4ZSnWxcSY,877
19
+ typefaster/infra/quote_loader.py,sha256=OPfjJ-6Mn1oeaAgZMcsIG6Yt4FePeRngccdO6mX0Jk4,1622
20
+ typefaster/infra/replay_store.py,sha256=dwkjHPFy9-yx3x9BC_THZoY8da3H3EChpQerzlnQGA8,474
21
+ typefaster/infra/repository.py,sha256=WS76T0tWsySV65oB6I4C8REFLoCF3aFlciB0pDoSQ2c,2026
22
+ typefaster/infra/sqlite_repository.py,sha256=XRtT8Q3tbi9cbuQIYCqfHly54rKF_koLT1Dw57R2OEM,14694
23
+ typefaster/net/__init__.py,sha256=tSx4UMBc0kUlp60corti6SvOnc9fJ_n5guJlysdRUZ0,74
24
+ typefaster/net/api.py,sha256=iD1AICf6NY8ykmE9hNNMmYNhFxnTve_jQpAzlSUTxzA,3957
25
+ typefaster/net/commands.py,sha256=V_l9R0UniZlWUMYfcOFTOXqs8arbRBcujI2CVYslxWU,6224
26
+ typefaster/net/token_store.py,sha256=MVTVX5kmW8wwCEJMwHAPzpUaBd2ULSo3oIIT0QNLBIo,1282
27
+ typefaster/services/__init__.py,sha256=4VngeItS-jc6-I71DsWkaWnvHODCni7ot4kiDICfMaw,79
28
+ typefaster/services/container.py,sha256=79AR_skykXGsneXZx-LZ6ihbV8x8yzWBBnTQIzeUzK4,1143
29
+ typefaster/services/daily_service.py,sha256=DpLGpa3Z53_-SOTYd4tTZB2VbwXJRyl8pwMZZoI6Iww,782
30
+ typefaster/services/ghost_service.py,sha256=U6XPMDtgi8ORvf6w-mGpyZf8aiHaDD35zZ6F3cjzSsw,1676
31
+ typefaster/services/profile_service.py,sha256=EquonTLismSHcs3HTQlUC_Rtt3uI3CT-Q5igFwRk220,473
32
+ typefaster/services/race_service.py,sha256=BmTMIpjkHX9Wom7PeaWRXHUHuZ4siaVtq-iey2tfwIQ,5406
33
+ typefaster/services/stats_service.py,sha256=yLP2hwubhqUr5rPxnxWX9n5PbINpgn9dQc1RANiYBeU,1912
34
+ typefaster/ui/__init__.py,sha256=IbB-ajsaTRD4HS4zy-fNbpkv8_broTEEhJgl_1d93q4,72
35
+ typefaster/ui/app.py,sha256=ki1Neeb2ErZiL3gvAgwwRJ1JPKa-H7hpZ_AJFaiibOE,3430
36
+ typefaster/ui/online_app.py,sha256=PHycCB32LGho8hHx9sfJ-o-gyiDT82CUTRRVF5ov33Y,678
37
+ typefaster/ui/theme.py,sha256=XzJFft9L3QP4qqUbbHmZ0v3ZeWodvcUY89PhsEHqlfs,1487
38
+ typefaster/ui/screens/__init__.py,sha256=-L4TEWNuG14Jofv6FNRLMKYGYoNquB1HvGmYV3YXYus,23
39
+ typefaster/ui/screens/_base.py,sha256=D-XqZBOwdINhtXUXjUfPReYA_aFRDuBAItGhXjH5Fmg,983
40
+ typefaster/ui/screens/daily.py,sha256=h2pvI4_vDQedEU-JRPRpLz6f8Bop39wW5FZoOSZ6kkY,2343
41
+ typefaster/ui/screens/help.py,sha256=OCzQlsGWzY7CwZMAcElGxMtBmao_KRjIFse7RuABtNE,788
42
+ typefaster/ui/screens/history.py,sha256=PQ0f5F7aHv1E6pmJvsNNYlbm59HthwAHGtYR0bX_i8s,1073
43
+ typefaster/ui/screens/leaderboard.py,sha256=kZhuixwIZLVsHaDjwsB258sQyMBZ9dYs2Y0QdRaFpKg,1968
44
+ typefaster/ui/screens/main_menu.py,sha256=DSJTHypdT32ieckVn5AoR7BQJfH_qxpHnJwUgR3SX3A,3435
45
+ typefaster/ui/screens/online_race.py,sha256=6fuUUyhMOk8wl6OC1LukQPsZxyfq_KZVPzL297I0mRQ,9718
46
+ typefaster/ui/screens/practice.py,sha256=6gvzVFYprGrbDClPArWM4COnzRi0zASrtnKjP6AREx8,2115
47
+ typefaster/ui/screens/profile.py,sha256=1QiUkhMBH38q3Vkw5kx4O1LUZc8vrNtKF8q4p8GABAk,995
48
+ typefaster/ui/screens/race.py,sha256=JlCxayx2W-fjnIza4ASK4Yg-UGkWSXDQv2YkndnxOMY,9961
49
+ typefaster/ui/screens/results.py,sha256=3nXSMEO9xIBEtKRoSzpGNBnRMbX3-yToXCQ1GuLJrZY,3280
50
+ typefaster/ui/screens/settings.py,sha256=nz0VRoRUmJxDj6CGFHiVtL0xDaz4lCvO1qa-l5NqXBE,2812
51
+ typefaster/ui/screens/stats.py,sha256=eePL3sU729EptA2DRJOlpezVUgOjE9zPkwT_dOSF0os,2342
52
+ typefaster/ui/widgets/__init__.py,sha256=D2YleDqZvFyzPhI501R-V8RmwDSSvOFRasAyVHpxUBo,56
53
+ typefaster/ui/widgets/bigtext.py,sha256=6naRaTuxke6tP0Q7j67KyvMsibTlRdhdiuEiszNdKYo,2162
54
+ typefaster/ui/widgets/live_stats.py,sha256=Q17tvAOHanhf4qS78G59sLEY-mieAUyDjyvAZN8tAaw,683
55
+ typefaster/ui/widgets/progress_bars.py,sha256=fZTh9a0KPVK1UnRq75Jf0YzSF4r85e3xn12oSUmIzmA,1392
56
+ typefaster/ui/widgets/typing_field.py,sha256=gYKOjytwsMEU_k5Nu9mnYLBP4smNEw4bZ6lR27_m-k0,934
57
+ typefaster_cli-0.1.0.dist-info/METADATA,sha256=PLwCZzbbJ9wtc02W9Rkl8OsH4X95sNHcuqR4sgUl1FE,6341
58
+ typefaster_cli-0.1.0.dist-info/WHEEL,sha256=mffPy8wBnZQn2VnJUU5jE99KsxaSfiyMHV9Yt0aLVxs,87
59
+ typefaster_cli-0.1.0.dist-info/entry_points.txt,sha256=-ix_zmJiKj-cikp7-QfkxqB6MPIYCD0hiGd36Q-XlS8,50
60
+ typefaster_cli-0.1.0.dist-info/licenses/LICENSE,sha256=ZrmIfNfrisYorCVzUnlZHoQZxJQB06oC_0lrNMkkXls,1079
61
+ typefaster_cli-0.1.0.dist-info/RECORD,,
@@ -0,0 +1,4 @@
1
+ Wheel-Version: 1.0
2
+ Generator: hatchling 1.30.1
3
+ Root-Is-Purelib: true
4
+ Tag: py3-none-any
@@ -0,0 +1,2 @@
1
+ [console_scripts]
2
+ typefaster = typefaster.cli:app
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 TYPEFASTER-CLI authors
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.