ft-tui 0.2.2__tar.gz
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.
- ft_tui-0.2.2/MANIFEST.in +4 -0
- ft_tui-0.2.2/PKG-INFO +496 -0
- ft_tui-0.2.2/README.md +479 -0
- ft_tui-0.2.2/pyproject.toml +39 -0
- ft_tui-0.2.2/setup.cfg +4 -0
- ft_tui-0.2.2/src/ft_tui/__init__.py +3 -0
- ft_tui-0.2.2/src/ft_tui/api/__init__.py +8 -0
- ft_tui-0.2.2/src/ft_tui/api/activity_types.py +120 -0
- ft_tui-0.2.2/src/ft_tui/api/campus_types.py +22 -0
- ft_tui-0.2.2/src/ft_tui/api/catalog_types.py +37 -0
- ft_tui-0.2.2/src/ft_tui/api/client_factory.py +17 -0
- ft_tui-0.2.2/src/ft_tui/api/client_interface.py +52 -0
- ft_tui-0.2.2/src/ft_tui/api/clubs_types.py +106 -0
- ft_tui-0.2.2/src/ft_tui/api/cluster_map_types.py +75 -0
- ft_tui-0.2.2/src/ft_tui/api/friends_types.py +204 -0
- ft_tui-0.2.2/src/ft_tui/api/goals_types.py +34 -0
- ft_tui-0.2.2/src/ft_tui/api/http_client.py +452 -0
- ft_tui-0.2.2/src/ft_tui/api/keychain.py +82 -0
- ft_tui-0.2.2/src/ft_tui/api/oauth_client.py +580 -0
- ft_tui-0.2.2/src/ft_tui/api/registration_types.py +106 -0
- ft_tui-0.2.2/src/ft_tui/api/slots_types.py +18 -0
- ft_tui-0.2.2/src/ft_tui/api/social_types.py +71 -0
- ft_tui-0.2.2/src/ft_tui/api/team_types.py +35 -0
- ft_tui-0.2.2/src/ft_tui/api/user_profile_types.py +153 -0
- ft_tui-0.2.2/src/ft_tui/cluster_map/__init__.py +1 -0
- ft_tui-0.2.2/src/ft_tui/cluster_map/experience.py +38 -0
- ft_tui-0.2.2/src/ft_tui/cluster_map/geometry.py +60 -0
- ft_tui-0.2.2/src/ft_tui/cluster_map/layout.json +1 -0
- ft_tui-0.2.2/src/ft_tui/cluster_map/state.py +93 -0
- ft_tui-0.2.2/src/ft_tui/config/__init__.py +1 -0
- ft_tui-0.2.2/src/ft_tui/config/settings.py +27 -0
- ft_tui-0.2.2/src/ft_tui/db/__init__.py +15 -0
- ft_tui-0.2.2/src/ft_tui/db/database.py +29 -0
- ft_tui-0.2.2/src/ft_tui/db/models.py +263 -0
- ft_tui-0.2.2/src/ft_tui/goto.py +97 -0
- ft_tui-0.2.2/src/ft_tui/i18n.py +95 -0
- ft_tui-0.2.2/src/ft_tui/integrations/__init__.py +1 -0
- ft_tui-0.2.2/src/ft_tui/integrations/zed_lsp.py +173 -0
- ft_tui-0.2.2/src/ft_tui/locales/es.json +1098 -0
- ft_tui-0.2.2/src/ft_tui/locales/fr.json +1115 -0
- ft_tui-0.2.2/src/ft_tui/main.py +72 -0
- ft_tui-0.2.2/src/ft_tui/services/__init__.py +1 -0
- ft_tui-0.2.2/src/ft_tui/services/catalog_cache.py +179 -0
- ft_tui-0.2.2/src/ft_tui/services/curriculum_selection.py +21 -0
- ft_tui-0.2.2/src/ft_tui/services/cursus.py +41 -0
- ft_tui-0.2.2/src/ft_tui/services/dashboard_cache.py +77 -0
- ft_tui-0.2.2/src/ft_tui/services/friends.py +165 -0
- ft_tui-0.2.2/src/ft_tui/services/fuzzy_search.py +77 -0
- ft_tui-0.2.2/src/ft_tui/services/git_service.py +92 -0
- ft_tui-0.2.2/src/ft_tui/services/pdf_checker.py +86 -0
- ft_tui-0.2.2/src/ft_tui/services/project_activity.py +49 -0
- ft_tui-0.2.2/src/ft_tui/services/project_catalog.py +21 -0
- ft_tui-0.2.2/src/ft_tui/services/project_checkout.py +191 -0
- ft_tui-0.2.2/src/ft_tui/services/project_curriculum.py +51 -0
- ft_tui-0.2.2/src/ft_tui/services/project_kind.py +15 -0
- ft_tui-0.2.2/src/ft_tui/services/project_navigation.py +144 -0
- ft_tui-0.2.2/src/ft_tui/services/project_presentation.py +93 -0
- ft_tui-0.2.2/src/ft_tui/services/project_registration.py +80 -0
- ft_tui-0.2.2/src/ft_tui/services/tester_catalog.py +127 -0
- ft_tui-0.2.2/src/ft_tui/services/tester_docker.py +437 -0
- ft_tui-0.2.2/src/ft_tui/services/tester_terminal.py +125 -0
- ft_tui-0.2.2/src/ft_tui/services/time_tracking.py +149 -0
- ft_tui-0.2.2/src/ft_tui/services/user_profile.py +42 -0
- ft_tui-0.2.2/src/ft_tui/services/user_settings.py +22 -0
- ft_tui-0.2.2/src/ft_tui/slots/__init__.py +1 -0
- ft_tui-0.2.2/src/ft_tui/slots/model.py +99 -0
- ft_tui-0.2.2/src/ft_tui/testers.toml +36 -0
- ft_tui-0.2.2/src/ft_tui/ui/__init__.py +1 -0
- ft_tui-0.2.2/src/ft_tui/ui/account_connection.py +239 -0
- ft_tui-0.2.2/src/ft_tui/ui/app.py +924 -0
- ft_tui-0.2.2/src/ft_tui/ui/button.py +238 -0
- ft_tui-0.2.2/src/ft_tui/ui/club_announcements.py +196 -0
- ft_tui-0.2.2/src/ft_tui/ui/club_inbox.py +50 -0
- ft_tui-0.2.2/src/ft_tui/ui/clubs.py +1007 -0
- ft_tui-0.2.2/src/ft_tui/ui/cluster_issue.py +234 -0
- ft_tui-0.2.2/src/ft_tui/ui/cluster_map.py +302 -0
- ft_tui-0.2.2/src/ft_tui/ui/cluster_map_grid.py +376 -0
- ft_tui-0.2.2/src/ft_tui/ui/cluster_portrait.py +295 -0
- ft_tui-0.2.2/src/ft_tui/ui/cluster_seat_preview.py +56 -0
- ft_tui-0.2.2/src/ft_tui/ui/cluster_workstation.py +493 -0
- ft_tui-0.2.2/src/ft_tui/ui/commands.py +138 -0
- ft_tui-0.2.2/src/ft_tui/ui/components.py +623 -0
- ft_tui-0.2.2/src/ft_tui/ui/correction_slots.py +319 -0
- ft_tui-0.2.2/src/ft_tui/ui/dashboard.py +599 -0
- ft_tui-0.2.2/src/ft_tui/ui/dev_log.py +263 -0
- ft_tui-0.2.2/src/ft_tui/ui/friend_project.py +227 -0
- ft_tui-0.2.2/src/ft_tui/ui/friend_search.py +129 -0
- ft_tui-0.2.2/src/ft_tui/ui/friend_suggestions.py +231 -0
- ft_tui-0.2.2/src/ft_tui/ui/friends.py +973 -0
- ft_tui-0.2.2/src/ft_tui/ui/layout.py +241 -0
- ft_tui-0.2.2/src/ft_tui/ui/modal.py +37 -0
- ft_tui-0.2.2/src/ft_tui/ui/navigation.py +27 -0
- ft_tui-0.2.2/src/ft_tui/ui/onboarding.py +249 -0
- ft_tui-0.2.2/src/ft_tui/ui/poke_notification.py +157 -0
- ft_tui-0.2.2/src/ft_tui/ui/profile_sections.py +124 -0
- ft_tui-0.2.2/src/ft_tui/ui/profile_view.py +171 -0
- ft_tui-0.2.2/src/ft_tui/ui/project_activity.py +400 -0
- ft_tui-0.2.2/src/ft_tui/ui/project_browser.py +176 -0
- ft_tui-0.2.2/src/ft_tui/ui/project_catalog_controller.py +288 -0
- ft_tui-0.2.2/src/ft_tui/ui/project_details.py +567 -0
- ft_tui-0.2.2/src/ft_tui/ui/project_navigation.py +173 -0
- ft_tui-0.2.2/src/ft_tui/ui/project_registration.py +229 -0
- ft_tui-0.2.2/src/ft_tui/ui/project_settings.py +146 -0
- ft_tui-0.2.2/src/ft_tui/ui/project_setup.py +183 -0
- ft_tui-0.2.2/src/ft_tui/ui/project_team.py +381 -0
- ft_tui-0.2.2/src/ft_tui/ui/project_technical_details.py +36 -0
- ft_tui-0.2.2/src/ft_tui/ui/project_testers.py +323 -0
- ft_tui-0.2.2/src/ft_tui/ui/push_events.py +97 -0
- ft_tui-0.2.2/src/ft_tui/ui/select.py +46 -0
- ft_tui-0.2.2/src/ft_tui/ui/settings.py +507 -0
- ft_tui-0.2.2/src/ft_tui/ui/shortcuts.py +77 -0
- ft_tui-0.2.2/src/ft_tui/ui/slots_calendar.py +233 -0
- ft_tui-0.2.2/src/ft_tui/ui/social.py +453 -0
- ft_tui-0.2.2/src/ft_tui/ui/styles/__init__.py +13 -0
- ft_tui-0.2.2/src/ft_tui/ui/styles/base.py +62 -0
- ft_tui-0.2.2/src/ft_tui/ui/styles/cluster_map.py +160 -0
- ft_tui-0.2.2/src/ft_tui/ui/styles/dashboard.py +98 -0
- ft_tui-0.2.2/src/ft_tui/ui/styles/dev_log.py +20 -0
- ft_tui-0.2.2/src/ft_tui/ui/styles/friends.py +45 -0
- ft_tui-0.2.2/src/ft_tui/ui/styles/projects.py +77 -0
- ft_tui-0.2.2/src/ft_tui/ui/styles/settings.py +35 -0
- ft_tui-0.2.2/src/ft_tui/ui/styles/user_profile.py +44 -0
- ft_tui-0.2.2/src/ft_tui/ui/terminal_capabilities.py +96 -0
- ft_tui-0.2.2/src/ft_tui/ui/user_profile.py +365 -0
- ft_tui-0.2.2/src/ft_tui/ui/username.py +26 -0
- ft_tui-0.2.2/src/ft_tui.egg-info/SOURCES.txt +123 -0
ft_tui-0.2.2/MANIFEST.in
ADDED
ft_tui-0.2.2/PKG-INFO
ADDED
|
@@ -0,0 +1,496 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: ft-tui
|
|
3
|
+
Version: 0.2.2
|
|
4
|
+
Summary: Offline-first terminal workspace for École 42 students
|
|
5
|
+
Requires-Python: >=3.12
|
|
6
|
+
Description-Content-Type: text/markdown
|
|
7
|
+
Requires-Dist: textual[syntax]<9.0,>=8.2
|
|
8
|
+
Requires-Dist: tree-sitter<0.26,>=0.25
|
|
9
|
+
Requires-Dist: tree-sitter-markdown<0.6,>=0.5
|
|
10
|
+
Requires-Dist: rich<15.0,>=13.7
|
|
11
|
+
Requires-Dist: SQLAlchemy<3.0,>=2.0
|
|
12
|
+
Requires-Dist: httpx<1.0,>=0.27
|
|
13
|
+
Requires-Dist: Pillow<14,>=11
|
|
14
|
+
Requires-Dist: textual-image[textual]<0.15,>=0.14.1
|
|
15
|
+
Requires-Dist: keyring<26.0,>=25.0
|
|
16
|
+
Requires-Dist: cryptography<46.0,>=42.0
|
|
17
|
+
|
|
18
|
+
# 42TUI
|
|
19
|
+
|
|
20
|
+
## Refresh the Intra project graph
|
|
21
|
+
|
|
22
|
+
The `project_data.json` used by the local TypeScript graph tools is served by
|
|
23
|
+
the **Projects website**, which uses a browser session. An API OAuth bearer
|
|
24
|
+
token does not create that website session. To refresh the graph automatically:
|
|
25
|
+
|
|
26
|
+
1. Install dependencies with `npm install`, then install the browser with
|
|
27
|
+
`npx playwright install chromium`. On a machine with a compatible Chromium
|
|
28
|
+
already installed, set `BROWSER_EXECUTABLE` instead.
|
|
29
|
+
2. Copy `.env.example` to `.env` and set `INTRA_LOGIN`, `INTRA_PASSWORD`,
|
|
30
|
+
`CURSUS_ID`, and `CAMPUS_ID`. Keep `.env` private; it is gitignored.
|
|
31
|
+
3. Run `bun fetch-graph-auto.ts`. The first login checks **Remember me** if
|
|
32
|
+
the 42 login page offers it. Later runs reuse the saved browser profile in
|
|
33
|
+
`.local/intra-graph-browser/`. If the session expires, the script logs in
|
|
34
|
+
again with the credentials in `.env`.
|
|
35
|
+
|
|
36
|
+
The output is `graph.<cursus>.<campus>.json`, which is gitignored. A failed
|
|
37
|
+
login or non-JSON response leaves the previous graph file in place. For a
|
|
38
|
+
scheduled server refresh, run the same command from the repository root with
|
|
39
|
+
a private `.env`; do not run overlapping jobs against the same browser profile.
|
|
40
|
+
If 42 asks for MFA or presents a challenge, the unattended login stops and
|
|
41
|
+
needs a fresh interactive authentication. `HEADLESS=false` opens a browser
|
|
42
|
+
for diagnosing a changed login flow.
|
|
43
|
+
|
|
44
|
+
42TUI is an offline-first Python TUI for managing projects, work time, and notes for École 42
|
|
45
|
+
students. Local project data remains in SQLite. Optional 42 account connection uses a centralized
|
|
46
|
+
Bun API with PostgreSQL, Redis, browser-based OAuth, and device-bound sessions; the local app does
|
|
47
|
+
not receive the user's 42 password or OAuth client secret. See the [OAuth backend operations
|
|
48
|
+
runbook](docs/42-oauth-backend-operations.md) to deploy that service.
|
|
49
|
+
|
|
50
|
+
## Getting started
|
|
51
|
+
|
|
52
|
+
### Production installation
|
|
53
|
+
|
|
54
|
+
Once the installer service and first PyPI release are live, install on Linux/macOS:
|
|
55
|
+
|
|
56
|
+
```sh
|
|
57
|
+
curl -fsSL https://tui.42wip.in/install.sh | sh
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
The installer uses uv, obtains Python >=3.12 when needed (including on Ubuntu
|
|
61
|
+
22.04 with system Python 3.10), and installs `42tui` in an isolated user environment.
|
|
62
|
+
Restart your terminal if needed, then launch `42tui`. Optional Nerd Font installation
|
|
63
|
+
is available with `--font`; you must select the font in your terminal settings.
|
|
64
|
+
|
|
65
|
+
Windows support is experimental; a PowerShell installer, exact-version installs,
|
|
66
|
+
upgrades, uninstall and migration from pipx are described in the
|
|
67
|
+
[production deployment guide](docs/production-deployment.md).
|
|
68
|
+
|
|
69
|
+
### Development from this checkout
|
|
70
|
+
|
|
71
|
+
Python 3.12 or newer is required (including the textual-image dependency).
|
|
72
|
+
|
|
73
|
+
```sh
|
|
74
|
+
python3.12 -m venv .venv
|
|
75
|
+
source .venv/bin/activate
|
|
76
|
+
pip install -r requirements.txt
|
|
77
|
+
42tui
|
|
78
|
+
```
|
|
79
|
+
|
|
80
|
+
For editable pipx installation, use the repository's `AGENTS.md` instructions
|
|
81
|
+
with an explicitly compatible Python interpreter.
|
|
82
|
+
|
|
83
|
+
The database is created under `$XDG_DATA_HOME/42tui/42tui.sqlite3` (or
|
|
84
|
+
`~/.local/share/42tui/42tui.sqlite3`). Existing projects remain available locally without an
|
|
85
|
+
account connection. Connect a 42 account in Settings to synchronize real projects from the
|
|
86
|
+
centralized API. Choosing **Save + Set Up Checkout** can clone the development repository URL
|
|
87
|
+
entered for a project. Uninstalling the application preserves this local data.
|
|
88
|
+
|
|
89
|
+
## Project browsing
|
|
90
|
+
|
|
91
|
+
The catalogue uses official project IDs while SQLite keeps its own IDs for notes,
|
|
92
|
+
timers and checkout folders. Shared API metadata, the cursus/campus web graph and
|
|
93
|
+
account progression are stored separately. Choose the cursus and campus in Settings.
|
|
94
|
+
Common Core views display **old + shared** or **new + shared** exclusively. When the
|
|
95
|
+
available evidence cannot select a track, choose one in Settings. Empty quests do
|
|
96
|
+
not establish the old track or rank zero. Track preferences
|
|
97
|
+
are scoped to the account, cursus and campus.
|
|
98
|
+
|
|
99
|
+
**My 42 projects** shows account projects in the selected track and the known current
|
|
100
|
+
and next Common Core ranks. Rank resolution is automatic and uses quest evidence
|
|
101
|
+
first. When rank quests are absent, an active, unvalidated rank-0 project can infer
|
|
102
|
+
rank 0 from personal progress (`source: project`). This requires fresh personal
|
|
103
|
+
sources and a fresh graph for the selected cursus/campus, a resolved track, and
|
|
104
|
+
no validated rank-0 project, higher-rank attempt, specialization attempt or missing
|
|
105
|
+
classification. Empty history alone never implies rank 0. Saved manual ranks are
|
|
106
|
+
ignored. Project ranks are inferred from graph rules, not official API levels.
|
|
107
|
+
**All projects** shows every Common Core project in the selected track until a
|
|
108
|
+
quest confirms Common Core completion, then shows specialization projects. An
|
|
109
|
+
unknown personal rank still permits browsing the full Common Core catalogue.
|
|
110
|
+
**Registerable projects**
|
|
111
|
+
uses only the account's registration API response, including ID-only projects whose
|
|
112
|
+
catalogue description is unavailable. **Specializations** groups branches within the
|
|
113
|
+
selected track. **Active and local projects** keeps local work accessible when the
|
|
114
|
+
track is unknown. Search, type filters and pagination apply to the selected view.
|
|
115
|
+
In **My 42 projects**, choosing a specific type browses all matching projects in
|
|
116
|
+
the selected track, including later ranks and specializations, even when your
|
|
117
|
+
personal rank is unknown. Returning to **All types** restores the current/next
|
|
118
|
+
rank view. Other views keep their section, registration or local-work limits.
|
|
119
|
+
The browser starts in **My 42 projects**. Loaded projects are sorted by ascending
|
|
120
|
+
rank, with exams first within each rank, then by name.
|
|
121
|
+
Unranked projects appear last. Exams have a dedicated badge and accent border.
|
|
122
|
+
Click a rank header or press Enter on it to collapse or expand the group.
|
|
123
|
+
Group state is kept during the session
|
|
124
|
+
for each account, cursus/campus and view.
|
|
125
|
+
Rank headers show completed/total counts for loaded projects matching the filters.
|
|
126
|
+
Headers and project names use status colors: green for completed, accent for
|
|
127
|
+
active, amber for partial progress or available, red for failed, and muted for
|
|
128
|
+
inactive. Project status `unknown` is displayed as **INACTIVE**.
|
|
129
|
+
|
|
130
|
+
Details show exams, web difficulty as XP (including zero), nullable duration,
|
|
131
|
+
classification provenance, structured requirements, recommendations and raw
|
|
132
|
+
unparsed rules. Drawing links are visual relationships, not prerequisites.
|
|
133
|
+
Project details show Solo or Group when matching session metadata is available,
|
|
134
|
+
including in cached views. Known group sizes appear as **Group · 3 members**;
|
|
135
|
+
missing or conflicting session counts remain unspecified. **Register** submits directly through the API; 42 validates
|
|
136
|
+
eligibility even when team mode is unknown. API registration requires the `projects`
|
|
137
|
+
scope and fresh project history and catalog data. Registration refreshes status and
|
|
138
|
+
prevents duplicate submissions; creating or joining a team is a separate step.
|
|
139
|
+
The Intra project link remains available for the complete requirements.
|
|
140
|
+
|
|
141
|
+
The backend publishes validated API/browser snapshots through a separate ingestion
|
|
142
|
+
worker. TUI reads never start a browser. Catalogue and branch caches are scoped by
|
|
143
|
+
cursus, campus and snapshot; personal observations are isolated by account. Offline
|
|
144
|
+
views show saved data with its collection time and availability. Registration is
|
|
145
|
+
fresh for five minutes, progression/quests for fifteen minutes and catalogue for
|
|
146
|
+
24 hours. An unavailable source preserves its prior observation as stale.
|
|
147
|
+
|
|
148
|
+
The TUI downloads all catalogue pages for both tracks and the unclassified section
|
|
149
|
+
of the selected cursus/campus into the existing SQLite database. Reopening the app
|
|
150
|
+
reuses that complete local copy for 24 hours; searches and project filters run
|
|
151
|
+
locally. Project details are saved on first selection and reused for the same
|
|
152
|
+
snapshot, including offline. Account progress still refreshes separately at startup.
|
|
153
|
+
Expired catalogues remain visible while a replacement downloads; failed or
|
|
154
|
+
interrupted downloads preserve the previous complete snapshot. No database reset
|
|
155
|
+
is needed for this cache.
|
|
156
|
+
|
|
157
|
+
The last known dashboard profile and goals are also saved locally. After a successful
|
|
158
|
+
sync, reopening with the same device credentials restores the selected cursus,
|
|
159
|
+
track, rank, Journey and account details before network refreshes complete. The
|
|
160
|
+
profile snapshot is bound to the backend and credential fingerprints; tokens and
|
|
161
|
+
private keys are never stored in this cache. Saved goals are marked stale while
|
|
162
|
+
refreshing, and pending or failed observations preserve the last known display.
|
|
163
|
+
|
|
164
|
+
To check the cache against synthetic HTTP responses, run
|
|
165
|
+
`postman simulation run postman/simulations/catalog-cache.sim.yaml --port 4901`
|
|
166
|
+
and, in another terminal,
|
|
167
|
+
`CATALOG_CACHE_SIMULATION_URL=http://127.0.0.1:4901/backend PYTHONPATH=src .venv/bin/python -m unittest tests.test_catalog_cache_simulation`.
|
|
168
|
+
|
|
169
|
+
|
|
170
|
+
The new SQLite schema refuses older databases with an explicit reset instruction.
|
|
171
|
+
Back up local notes and activity before intentionally deleting an old database;
|
|
172
|
+
the application never resets it automatically.
|
|
173
|
+
|
|
174
|
+
## Project testers
|
|
175
|
+
|
|
176
|
+
In **Projects**, use the **Testers** button beside the project name or press `t`
|
|
177
|
+
to reveal and focus the tester controls. The **Testers** section offers **Install Francinette**, then
|
|
178
|
+
**Run Francinette**, for `libft`, `ft_printf` and `get_next_line` (including their
|
|
179
|
+
`42cursus-` slugs). One image serves all three projects. Installation needs no
|
|
180
|
+
project folder; running needs an existing local folder configured with `e`.
|
|
181
|
+
Born2beroot has no bundled tester yet.
|
|
182
|
+
|
|
183
|
+
42TUI fetches a pinned commit of
|
|
184
|
+
[WaRtr0/francinette-image](https://github.com/WaRtr0/francinette-image) and builds
|
|
185
|
+
its Dockerfile, without running its installer or editing shell configuration.
|
|
186
|
+
Docker uses `linux/amd64` or `linux/arm64` on Linux and macOS with x86_64
|
|
187
|
+
(Intel/AMD) or ARM64 processors. Francinette prefers the Docker engine's native
|
|
188
|
+
architecture, avoiding x86_64 emulation on ARM64. This also respects the CPU of
|
|
189
|
+
a remote Docker engine. 32-bit x86 is unsupported.
|
|
190
|
+
Docker Engine, Docker Desktop and other compatible local engines use the standard
|
|
191
|
+
Docker CLI. The active context and `DOCKER_*` variables are respected and kept
|
|
192
|
+
for the entire operation, including cleanup. A Linux engine is required.
|
|
193
|
+
Before running, 42TUI checks the selected container architecture and reads/writes a temporary probe
|
|
194
|
+
inside the project folder to verify local file sharing and permissions.
|
|
195
|
+
On Linux, a regular daemon runs testers with your UID/GID; rootless Docker retains
|
|
196
|
+
its mapped container root. Francinette uses a private writable copy of its code
|
|
197
|
+
for logs and caches, so generated project files remain owned by your account.
|
|
198
|
+
During each run, private Clang wrappers default debug information to DWARF 4,
|
|
199
|
+
compatible with the upstream Ubuntu 22.04 image's Valgrind. Memory checks remain
|
|
200
|
+
enabled; compiler arguments and explicit debug-format options are preserved.
|
|
201
|
+
The wrappers and writable tester copy are removed when the run finishes.
|
|
202
|
+
The upstream Dockerfile fetches dependencies at build time; pinning the wrapper
|
|
203
|
+
repository does not freeze those dependencies. The temporary container mounts the
|
|
204
|
+
project read/write at `/project` and runs Francinette from its writable copy,
|
|
205
|
+
keeping `/project` as the working directory.
|
|
206
|
+
Tester output appears in the terminal; press any key to return to 42TUI. Ctrl+C
|
|
207
|
+
interrupts the operation and removes its temporary container. An exit status of
|
|
208
|
+
zero means the tester finished; inspect its output for individual test results.
|
|
209
|
+
Logs are saved under `$XDG_DATA_HOME/42tui/testers/logs`, or
|
|
210
|
+
`~/.local/share/42tui/testers/logs`. Failed operations offer **Retry**.
|
|
211
|
+
|
|
212
|
+
The small **×** button beside a tester removes its Docker images (all installed
|
|
213
|
+
versions and CPU architectures) and stopped 42TUI test/probe containers. It also
|
|
214
|
+
works without a configured project folder. Since the installation is shared,
|
|
215
|
+
removing Francinette affects all three projects; use **Install Francinette** to
|
|
216
|
+
restore it. Running containers, project files, other testers and Docker volumes
|
|
217
|
+
are left in place. Docker can retain shared layers and build cache, so removing
|
|
218
|
+
the images does not guarantee reclaiming their entire displayed size. See
|
|
219
|
+
[Docker's resource cleanup documentation](https://docs.docker.com/engine/manage-resources/pruning/).
|
|
220
|
+
|
|
221
|
+
42TUI reuses working Docker installations. Otherwise it distinguishes a missing
|
|
222
|
+
CLI, a stopped engine and denied access. **Check Docker access again** retries
|
|
223
|
+
detection after setup. On macOS, **Install Docker** offers Homebrew when available,
|
|
224
|
+
or downloads and opens the official installer for the host processor. Finish
|
|
225
|
+
[Docker Desktop setup](https://docs.docker.com/desktop/setup/install/mac-install/)
|
|
226
|
+
yourself and start Docker before rechecking. On supported Ubuntu and Debian
|
|
227
|
+
releases, the confirmation dialog lists the commands for Docker's official apt
|
|
228
|
+
repository before any administrator command runs. Conflicting system packages
|
|
229
|
+
must be resolved manually; 42TUI does not remove them. Other distributions link to
|
|
230
|
+
the official installation instructions. For access problems, follow the
|
|
231
|
+
[Linux post-install steps](https://docs.docker.com/engine/install/linux-postinstall/),
|
|
232
|
+
then sign out and back in; never run the entire application with `sudo`.
|
|
233
|
+
|
|
234
|
+
The bundled `ft_tui/testers.toml` is loaded at startup and extended by
|
|
235
|
+
`$XDG_CONFIG_HOME/42tui/testers.toml`, falling back to `~/.config/42tui/testers.toml`.
|
|
236
|
+
Each user entry replaces the entire entry with the same `id`; multiple entries
|
|
237
|
+
may target a project. Disable a bundled tester with:
|
|
238
|
+
|
|
239
|
+
```toml
|
|
240
|
+
[[testers]]
|
|
241
|
+
id = "francinette"
|
|
242
|
+
enabled = false
|
|
243
|
+
```
|
|
244
|
+
|
|
245
|
+
To add a tester, copy this template and replace the repository, full commit SHA,
|
|
246
|
+
Docker build context and command with real values. The command is an argument
|
|
247
|
+
array executed inside the container, without shell expansion. Repository URLs must
|
|
248
|
+
use HTTPS, context paths must remain inside the repository, and platforms are
|
|
249
|
+
`linux` and/or `darwin`:
|
|
250
|
+
|
|
251
|
+
```toml
|
|
252
|
+
[[testers]]
|
|
253
|
+
id = "my-tester"
|
|
254
|
+
name = "My tester"
|
|
255
|
+
projects = ["libft", "42cursus-ft_printf"]
|
|
256
|
+
platforms = ["linux", "darwin"]
|
|
257
|
+
repository = "https://github.com/your-account/your-tester.git"
|
|
258
|
+
ref = "0123456789abcdef0123456789abcdef01234567"
|
|
259
|
+
context = "."
|
|
260
|
+
command = ["/tester/run", "--verbose"]
|
|
261
|
+
```
|
|
262
|
+
|
|
263
|
+
Restart 42TUI to reload changes. Invalid user TOML or entries display an error and
|
|
264
|
+
leave the complete bundled catalog usable. Image identities include the tester,
|
|
265
|
+
commit, repository and context; availability is always checked against Docker.
|
|
266
|
+
ARM64 images have a separate tag, so existing amd64 images are preserved.
|
|
267
|
+
On ARM64, install the native image once from the tester controls after upgrading.
|
|
268
|
+
Optional `architectures = ["amd64", "arm64"]` allows native execution for custom
|
|
269
|
+
testers that support both CPUs. Existing custom entries default to `["amd64"]`;
|
|
270
|
+
a tester restricted to one CPU still requires emulation on other CPUs. When the
|
|
271
|
+
engine's CPU is unavailable, the first configured architecture is used.
|
|
272
|
+
See the [verification report](docs/testers-validation.md) for tested environments
|
|
273
|
+
and remaining platform checks.
|
|
274
|
+
|
|
275
|
+
## Project checkout setup
|
|
276
|
+
|
|
277
|
+
In **Projects**, select a project and press `e`. Choose its local folder and optional
|
|
278
|
+
development remote, then select **Save + Set Up Checkout**. 42TUI clones the development remote
|
|
279
|
+
into a missing or empty folder, or initializes an empty local Git repository when no development
|
|
280
|
+
remote is set. Existing repositories are preserved; non-empty non-Git folders and conflicting
|
|
281
|
+
remote URLs are rejected.
|
|
282
|
+
|
|
283
|
+
When the checkout has commits, setup ensures local `dev` and `master` branches. A cloned checkout
|
|
284
|
+
switches to `dev`, associated with `origin/dev`. `master` is reserved for Vogsphere, whose accepted
|
|
285
|
+
submission branch is `master`; once the Intra API supplies its URL, 42TUI configures
|
|
286
|
+
`vogsphere/master`. No branch is pushed automatically. Add a real development URL yourself if you
|
|
287
|
+
want setup to clone it. For a new empty local repository, Git cannot store a
|
|
288
|
+
`master` branch before the first commit; run setup again after creating that commit to create
|
|
289
|
+
`master`.
|
|
290
|
+
|
|
291
|
+
In project details, **Start timer** records a manual work session in SQLite. A running timer
|
|
292
|
+
survives closing and reopening 42TUI; starting a timer for another project stops the previous
|
|
293
|
+
one. Subject checks run at the interval selected in Settings (six hours by default). 42TUI looks
|
|
294
|
+
for `subject.pdf` at the project root, calculates its SHA-256, and asks the configured provider
|
|
295
|
+
for a reference fingerprint. The backend queues subject work for the unified Intra worker,
|
|
296
|
+
which uses the same authenticated browser profile as Graph and Pace. It discovers the subject
|
|
297
|
+
link on the project page, prefers English, validates the PDF, and caches it with its SHA-256
|
|
298
|
+
in Redis. The HTTP handler never opens a browser or calls the restricted attachments endpoint.
|
|
299
|
+
The TUI shows `SUBJECT · QUEUED` while waiting and checks for the result every 30 seconds.
|
|
300
|
+
Missing or different PDFs are atomically installed as `subject.pdf`; failed downloads preserve
|
|
301
|
+
the existing file. Configured folders work with or without a Git repository. Worker snapshots
|
|
302
|
+
are fresh for six hours and retained for 24 hours. PDF contents are not extracted.
|
|
303
|
+
Project setup adds `*.pdf` to `.gitignore` so a downloaded untracked
|
|
304
|
+
subject isn't accidentally committed; PDFs already tracked by Git remain tracked until removed manually.
|
|
305
|
+
Use **Open subject** to launch the PDF with the OS default application or TDF, selected in Settings.
|
|
306
|
+
TDF must be installed separately and available on `PATH`.
|
|
307
|
+
|
|
308
|
+
## Keyboard shortcuts
|
|
309
|
+
|
|
310
|
+
- `j` / `k`: move down or up through projects
|
|
311
|
+
- `h` / `l`: switch to the previous or next tab
|
|
312
|
+
- `e`: set up or edit the selected project's local path and development remote
|
|
313
|
+
- `Home` / `End`: select the first or last project
|
|
314
|
+
- `Ctrl+D` / `Ctrl+U`: move down or up one page
|
|
315
|
+
- `F1`–`F5`: help, projects, notes, Git Sync, and settings
|
|
316
|
+
- `q`: quit
|
|
317
|
+
|
|
318
|
+
In **Settings**, choose a favorite editor (Neovim, Vim, Visual Studio Code, or Zed) and a favorite
|
|
319
|
+
Textual theme. From **Projects**, select a configured project and press `o` or use **Open in editor**
|
|
320
|
+
to open its folder. Terminal editors temporarily take over the terminal; GUI editors open separately.
|
|
321
|
+
|
|
322
|
+
In **Settings → Appearance**, image rendering defaults to **Automatic**: 42TUI uses
|
|
323
|
+
the Sixel or Kitty graphics backend selected by `textual-image`, with colored Unicode
|
|
324
|
+
blocks as a fallback. You can force either protocol, select colored blocks, or turn
|
|
325
|
+
images off. Forced protocols require support in the terminal and any multiplexer
|
|
326
|
+
between it and 42TUI. Rendering is selected by protocol support, not CPU/GPU hardware.
|
|
327
|
+
|
|
328
|
+
Icons have independent **Automatic**, **Nerd Font**, **Simple Unicode**, and **Text only**
|
|
329
|
+
modes, with a preview. Automatic mode uses terminal/environment hints, not a scan of
|
|
330
|
+
installed fonts: those do not establish which font the terminal uses, especially over
|
|
331
|
+
SSH. `NERDFONT=1` or `NERDFONT=0` overrides the automatic estimate. An explicit icon
|
|
332
|
+
choice in Settings takes precedence. These preferences are saved locally and apply
|
|
333
|
+
without restarting; font shape and pixel size remain controlled by the terminal.
|
|
334
|
+
|
|
335
|
+
## `42goto` shell navigation
|
|
336
|
+
|
|
337
|
+
`42goto` changes the current shell directory to a project's configured local folder. Enable the
|
|
338
|
+
shell function and project completion in your shell startup file:
|
|
339
|
+
|
|
340
|
+
```sh
|
|
341
|
+
# Bash: add to ~/.bashrc
|
|
342
|
+
eval "$(command 42goto --shell-init bash)"
|
|
343
|
+
|
|
344
|
+
# Zsh: add to ~/.zshrc
|
|
345
|
+
eval "$(command 42goto --shell-init zsh)"
|
|
346
|
+
```
|
|
347
|
+
|
|
348
|
+
Then run `42goto` to jump to the currently active project, or `42goto <project>` to open a project
|
|
349
|
+
by its name or slug. Press Tab to complete configured project slugs. A fresh editor heartbeat
|
|
350
|
+
defines the active project; when there is no fresh heartbeat, the most recently tracked project
|
|
351
|
+
is used. If there is no activity history, a single configured in-progress project is used; otherwise
|
|
352
|
+
42goto asks you to specify the project. Project folders are read from the same local SQLite database
|
|
353
|
+
as 42TUI. After updating a source checkout, reinstall the package to refresh the CLI entry points:
|
|
354
|
+
|
|
355
|
+
```sh
|
|
356
|
+
pip install -e .
|
|
357
|
+
```
|
|
358
|
+
|
|
359
|
+
## Editor activity tracking
|
|
360
|
+
|
|
361
|
+
42TUI accepts local activity heartbeats from editor plugins, even when the TUI is closed. Configure
|
|
362
|
+
the project folder in 42TUI first, then have an editor integration call the shared CLI command when
|
|
363
|
+
the active file changes, is edited, or is saved:
|
|
364
|
+
|
|
365
|
+
```sh
|
|
366
|
+
42tui heartbeat --file /absolute/path/to/project/src/main.c --editor nvim --quiet
|
|
367
|
+
42tui heartbeat --file /absolute/path/to/project/src/main.c --editor vscode --write --quiet
|
|
368
|
+
```
|
|
369
|
+
|
|
370
|
+
The CLI finds the deepest configured project folder containing the file. It stores the project,
|
|
371
|
+
editor, event time, and save flag in SQLite; it does not store the file path or read file contents.
|
|
372
|
+
Elapsed time is attributed between recent heartbeats, with a 120-second idle cutoff and a shared
|
|
373
|
+
timeline across editors to prevent double counting. Files outside configured project folders are
|
|
374
|
+
ignored with an error. Editor-specific plugins can call this command, so each integration only
|
|
375
|
+
needs to report its active file and editor ID; installing the app itself requires no network access.
|
|
376
|
+
See [`integrations/`](integrations/README.md) for the current VS Code, Zed, Vim, and Neovim adapters.
|
|
377
|
+
|
|
378
|
+
## Roadmap
|
|
379
|
+
|
|
380
|
+
Implemented for the local MVP:
|
|
381
|
+
|
|
382
|
+
- Local project catalog, dashboard, and SQLite persistence; centralized API project sync after
|
|
383
|
+
OAuth connection
|
|
384
|
+
- Profile settings, default projects folder, and per-project local path / development remote setup
|
|
385
|
+
- Project navigation, Git hygiene, manual timer, PDF fingerprinting, and Markdown dev log
|
|
386
|
+
- Local editor heartbeat ingestion and project-level coding time aggregation
|
|
387
|
+
- Dashboard activity streaks, four-week heatmap, peak coding day/hour, and rolling validation pace against Black Hole deadlines
|
|
388
|
+
- Editor integrations for VS Code, Zed, Vim, and Neovim
|
|
389
|
+
- First-run onboarding for profile, projects folder, editor, theme, and language
|
|
390
|
+
- English, French, and Spanish interface with JSON catalogs and English fallback
|
|
391
|
+
|
|
392
|
+
Available with the optional OAuth backend:
|
|
393
|
+
|
|
394
|
+
- Browser-based 42 account pairing and profile/project synchronization, while preserving local SQLite data
|
|
395
|
+
|
|
396
|
+
Planned:
|
|
397
|
+
- Fetch and display achievement stars for outstanding project evaluations
|
|
398
|
+
- Automatically fetch the official Vogsphere remote from 42 Intra
|
|
399
|
+
- **Peer-Review Matcher:** use the 42 API to show available defense slots, consider correction points and availability, notify about useful openings, and keep an evaluation history with grades and feedback
|
|
400
|
+
- **Cluster Map:** simulate the campus's three clusters as a seat map, showing per-seat occupancy and available seats, plus the 42 profile and project information available for each seated student
|
|
401
|
+
- **Friends & Study Groups:** use the 42 API to automatically sync friends' project enrollment and progress, with activity and completion feeds, Black Hole pace comparisons, and cluster presence
|
|
402
|
+
|
|
403
|
+
## Source layout
|
|
404
|
+
|
|
405
|
+
Run the service regression tests from the project root after installing the package:
|
|
406
|
+
|
|
407
|
+
```sh
|
|
408
|
+
python -m unittest discover -s tests -v
|
|
409
|
+
```
|
|
410
|
+
|
|
411
|
+
```text
|
|
412
|
+
src/ft_tui/
|
|
413
|
+
├── api/ # centralized API client and provider contract
|
|
414
|
+
├── config/ # local paths and settings
|
|
415
|
+
├── db/ # models and SQLite access
|
|
416
|
+
├── services/ # business logic independent from the UI
|
|
417
|
+
├── ui/ # Textual application and views
|
|
418
|
+
├── locales/ # English-fallback JSON catalogs (French and Spanish)
|
|
419
|
+
└── main.py # CLI entry point
|
|
420
|
+
```
|
|
421
|
+
|
|
422
|
+
To add another interface language, add `src/ft_tui/locales/<code>.json` with
|
|
423
|
+
English source messages as keys, then register its code and display name in
|
|
424
|
+
`ft_tui/i18n.py`. Any untranslated message falls back to English.
|
|
425
|
+
|
|
426
|
+
### Project attempts and evaluations
|
|
427
|
+
|
|
428
|
+
For a connected account, select a project and open **Attempts & evaluations**
|
|
429
|
+
from Project Details. The dedicated screen selects the current team and lets you
|
|
430
|
+
browse previous attempts, members, scores, repository and lock/closure dates.
|
|
431
|
+
Expand peer corrections to read comments and available feedback; expand automated
|
|
432
|
+
evaluations to read their output. Scheduled, overdue/unfilled and completed
|
|
433
|
+
corrections have separate labels. Missing marks remain distinct from zero.
|
|
434
|
+
|
|
435
|
+
Use arrow keys to select an attempt, Tab/Enter to expand an evaluation, `r` to
|
|
436
|
+
refresh, `i` to open Intra, and Escape to return to the selected project. The
|
|
437
|
+
layout stacks on terminals narrower than 90 columns. Activity, evaluation details,
|
|
438
|
+
automated output and embedded team results are cached by account for up to 30 days. Reopening the screen reuses these observations;
|
|
439
|
+
press Refresh to fetch changes from Intra. Cached sources show their collection
|
|
440
|
+
time separately; unavailable fields and hidden participants stay explicit.
|
|
441
|
+
Team management, booking and feedback submission remain planned in `TODO.md`.
|
|
442
|
+
|
|
443
|
+
Both the updated backend and TUI are required. Live 42 response parity remains
|
|
444
|
+
unverified. See [the HTTP simulation instructions](postman/PROJECT_ACTIVITY_SIMULATION.md)
|
|
445
|
+
for local integration verification and the sandbox limitation.
|
|
446
|
+
|
|
447
|
+
## Friends and rankings
|
|
448
|
+
|
|
449
|
+
Connect your 42 account and select a cursus in Settings, then open Friends.
|
|
450
|
+
Add friends by their 42 login to see their level, ongoing projects and recent
|
|
451
|
+
cluster location. Open a friend for project history or “Compare with me” to find
|
|
452
|
+
shared projects and opportunities to help each other. Rankings compares cursus
|
|
453
|
+
working curriculum ranks among your friends and yourself, with equal places for tied ranks. Published old/new cursus project mappings determine the estimate; level remains visible.
|
|
454
|
+
|
|
455
|
+
Search and filters cover shared projects, campus membership and active cluster
|
|
456
|
+
sessions. Enter opens a profile; Escape returns to the list. Narrow terminals
|
|
457
|
+
show one pane at a time. Saved progression remains available offline, while
|
|
458
|
+
cluster presence always requires a recent successful lookup. Refresh runs in the
|
|
459
|
+
background while the tab is open; progression caches refresh every 15 minutes
|
|
460
|
+
and presence every minute. The updated backend must be deployed before using
|
|
461
|
+
online Friends. Campus rankings and historical growth remain follow-up work in
|
|
462
|
+
[TODO.md](TODO.md).
|
|
463
|
+
|
|
464
|
+
Search in Friends and Projects accepts abbreviations and small typos, ignores
|
|
465
|
+
case, accents and separators, and supports multiple words in any order. For
|
|
466
|
+
example, `pswp` or `puhs swap` finds `push_swap`. Friends search also matches
|
|
467
|
+
display names, campuses and ongoing projects; rankings retain their original
|
|
468
|
+
positions when filtered. Project search behaves consistently offline and in
|
|
469
|
+
catalog results. Deploy the updated backend to enable fuzzy catalog queries.
|
|
470
|
+
|
|
471
|
+
The Add friend field searches a shared student directory stored in the backend.
|
|
472
|
+
Type at least two characters, press Down to browse matches and Enter to choose
|
|
473
|
+
a login, then select Add friend. It matches names, abbreviations and small typos,
|
|
474
|
+
excludes existing friends and your account, and works across campuses. Results
|
|
475
|
+
show when the index is building or stale. Selecting Add verifies the numeric user
|
|
476
|
+
ID and handles renamed logins. Exact-login entry remains available. Deploy the
|
|
477
|
+
new migration and run the [directory worker](backend/README.md#add-friend-suggestions)
|
|
478
|
+
to populate and refresh the index; typing never scans the 42 API.
|
|
479
|
+
|
|
480
|
+
|
|
481
|
+
## Clubs
|
|
482
|
+
|
|
483
|
+
Open Clubs to create an invite-only campus group, add registered 42TUI students,
|
|
484
|
+
and recruit players with Babyfoot, Smash or custom templates. Members join or
|
|
485
|
+
waitlist; organizers start ready activities manually. Club managers and configured
|
|
486
|
+
campus representatives can publish announcements.
|
|
487
|
+
|
|
488
|
+
The global DND button (`Ctrl+Shift+D`) suspends Clubs notifications. Each club
|
|
489
|
+
has its own bell; added members and the campus group start muted. Invitations
|
|
490
|
+
appear at the top right without taking focus (`Ctrl+Shift+N` focuses them).
|
|
491
|
+
Activities and announcements remain browsable while muted. Spontaneous popups
|
|
492
|
+
require fresh campus presence on an unlocked workstation.
|
|
493
|
+
|
|
494
|
+
Apply the Clubs database migration and restart both backend and TUI before use.
|
|
495
|
+
See the [MVP guide](docs/clubs-mvp.md) for deployment, current limitations and
|
|
496
|
+
verification, and the [roadmap](docs/clubs-roadmap.md) for later phases.
|