initial commit
This commit is contained in:
@@ -0,0 +1,182 @@
|
||||
# Piscine Graph Tweaks
|
||||
|
||||
Firefox extension for the EPITA piscine exercise graph (`exercises_c`,
|
||||
`exercises_shell`, …) on `intra.forge.epita.fr`. Two things:
|
||||
|
||||
**A progress panel**, bottom right of the page:
|
||||
|
||||
- **validated / total** with the percentage and a segmented bar;
|
||||
- a separate **required** count, which is the one that actually matters;
|
||||
- breakdown: validated, required left, bonus left, locked.
|
||||
|
||||
**A full screen view of the graph**, because the page crams a ~5000px-wide
|
||||
diagram into a 200px-tall box:
|
||||
|
||||
- opens at a readable zoom instead of shrinking everything to fit;
|
||||
- **search** — dims everything but the matches, and recentres when there is only one;
|
||||
- **minimap** bottom right, coloured by status; click or drag it to jump anywhere;
|
||||
- **arrow keys** to pan (Shift for 3×), wheel to zoom, shift+wheel to pan sideways,
|
||||
drag to pan, double-click to zoom in, click a node to open the exercise;
|
||||
- `/` search · `Home` readable view · `0` fit everything · `+` / `−` zoom · `Esc` close.
|
||||
|
||||
No network request is made: everything is read from the page itself.
|
||||
|
||||
## How it reads the graph
|
||||
|
||||
The graph is a mermaid `stateDiagram` rendered as inline SVG, and each node keeps
|
||||
its whole state in its `id`:
|
||||
|
||||
```
|
||||
state-"_required=true/_validated=true/_accessible=true/…/exercises_c/clang~format"-0
|
||||
```
|
||||
|
||||
So the three booleans are parsed straight from the id — no color guessing — and
|
||||
`~` is mermaid's escape for `-` in the path. Status mapping:
|
||||
|
||||
| `_validated` | `_accessible` | `_required` | status |
|
||||
|--------------|---------------|-------------|---------------|
|
||||
| `true` | – | – | validated |
|
||||
| `false` | `false` | – | locked |
|
||||
| `false` | `true` | `true` | required left |
|
||||
| `false` | `true` | `false` | bonus left |
|
||||
|
||||
Verified against the live page: 91 nodes parsed, the only skipped element being
|
||||
mermaid's `state-root_start-1` marker.
|
||||
|
||||
## Why there is no "started but unfinished" state
|
||||
|
||||
There is none in the page. Each node carries exactly those three booleans, and
|
||||
`_validated` only flips once everything passes. An exercise started this morning
|
||||
and one never opened produce **byte-for-byte identical markup** — compare
|
||||
`gdb_wristwatch` and `test_a_bit`, both `_required=true/_validated=false/
|
||||
_accessible=true`. So per-exercise completion cannot come from this page, and the
|
||||
extension does not pretend otherwise.
|
||||
|
||||
If you ever want real "started" detection, it has to come from the GitLab API
|
||||
rather than the graph — one call listing the exercise projects and their
|
||||
`last_activity_at` would say which ones you have pushed to:
|
||||
|
||||
```
|
||||
/api/v4/groups/<url-encoded group path>/projects?per_page=100&order_by=last_activity_at
|
||||
```
|
||||
|
||||
That is one request for the whole graph, not one per exercise. Not wired in: it
|
||||
needs checking against the real API response first.
|
||||
|
||||
## How the full screen view works
|
||||
|
||||
It **moves** the real `<svg>` into the overlay rather than cloning it (a
|
||||
placeholder holds its spot in the page), so mermaid's own `<style>` block — which
|
||||
lives inside the SVG and is scoped to its id — keeps working untouched. It then
|
||||
takes over the `svg-pan-zoom` viewport `<g>` transform instead of fighting that
|
||||
instance. Inherited CSS custom properties (`--required-validated` and friends) do
|
||||
not survive the move, so they are resolved and re-declared on the overlay first.
|
||||
Closing puts every attribute back exactly as it was.
|
||||
|
||||
---
|
||||
|
||||
## Installing (for you, while developing)
|
||||
|
||||
1. Open `about:debugging#/runtime/this-firefox`
|
||||
2. **Load Temporary Add-on…**
|
||||
3. Pick this folder's `manifest.json`
|
||||
4. Open the graph page — the panel shows up bottom right
|
||||
|
||||
"Temporary" means it **disappears when Firefox restarts**. That is the
|
||||
development mode: after each code change, hit **Reload** on `about:debugging`.
|
||||
For a permanent install, use the same route as your friends below.
|
||||
|
||||
## Sharing it with friends (without publishing to the store)
|
||||
|
||||
Build the package:
|
||||
|
||||
```sh
|
||||
./build.sh # -> dist/piscine-graph-tweaks-<version>.zip and .xpi
|
||||
```
|
||||
|
||||
Then pick one of three routes. The catch: **Firefox release refuses to install an
|
||||
unsigned extension permanently**, so a `.xpi` dropped in a Discord channel will
|
||||
not install by double-click.
|
||||
|
||||
### Option A — the zip plus temporary loading (simplest, zero setup)
|
||||
|
||||
Send the `.zip`, they unpack it, then `about:debugging` → *Load Temporary
|
||||
Add-on…* → `manifest.json`.
|
||||
|
||||
- works on any Firefox, right away
|
||||
- has to be redone every time the browser restarts
|
||||
|
||||
### Option B — "unlisted" signing on AMO (best for daily use)
|
||||
|
||||
Mozilla signs the extension **without publishing it** in the catalogue: it stays
|
||||
unlisted, you distribute the `.xpi` yourself, and it installs permanently on a
|
||||
normal Firefox.
|
||||
|
||||
1. Create an account on https://addons.mozilla.org, then generate an API key at
|
||||
https://addons.mozilla.org/developers/addon/api/key/
|
||||
2. Sign it:
|
||||
|
||||
```sh
|
||||
npm install --global web-ext
|
||||
web-ext sign --channel=unlisted \
|
||||
--api-key="$AMO_JWT_ISSUER" --api-secret="$AMO_JWT_SECRET"
|
||||
```
|
||||
|
||||
3. You get a **signed** `.xpi` in `web-ext-artifacts/`. Friends open it with
|
||||
Firefox (drag into the window, or `Ctrl+O`) → *Add*.
|
||||
|
||||
The extension id is already pinned in `manifest.json`
|
||||
(`piscine-graph-tweaks@valentin`) — required for signing, and it must not change
|
||||
after the first signature.
|
||||
|
||||
### Option C — Firefox Developer Edition / Nightly / ESR
|
||||
|
||||
On those builds only, signature enforcement can be turned off: `about:config` →
|
||||
`xpinstall.signatures.required` → `false`, then install the unsigned `.xpi`.
|
||||
Pointless on Firefox release, where the pref is ignored.
|
||||
|
||||
---
|
||||
|
||||
## Layout
|
||||
|
||||
```
|
||||
manifest.json extension manifest (MV3) and the matched URLs
|
||||
src/content.js node parsing, stats, the progress panel
|
||||
src/viewer.js full screen view: pan/zoom, search, minimap
|
||||
src/panel.css every injected style
|
||||
src/inject.js injects the network hook into the page world
|
||||
src/net-hook.js records what the page fetches (discovery aid)
|
||||
src/icon.svg icon
|
||||
tools/dump-graph.js console snippet: parsed nodes, works without the extension
|
||||
tools/make-fixture.js console snippet: captures the graph for offline testing
|
||||
tests/build-harness.js rebuilds tests/harness.html from a fixture + current src
|
||||
tests/shot.sh screenshots the harness with headless Firefox
|
||||
build.sh produces dist/*.zip and dist/*.xpi
|
||||
```
|
||||
|
||||
`src/viewer.js` must load **before** `src/content.js` — the panel's button calls
|
||||
`window.__pgtViewer`.
|
||||
|
||||
## Testing
|
||||
|
||||
Two ways, both offline-friendly:
|
||||
|
||||
- **Against a captured page.** Run `tools/make-fixture.js` in the console on the
|
||||
graph page, save it with `wl-paste > tests/fixture.json`, then
|
||||
`node tests/build-harness.js && tests/shot.sh`. The harness inlines the current
|
||||
`src/` so any edit is one command away from a screenshot.
|
||||
- **Against the live page.** Anything driving a real browser works; the parser and
|
||||
both screenshots in this repo's history were checked that way.
|
||||
|
||||
## Other projects
|
||||
|
||||
The manifest already matches `*/root/exercises_*` on `intra.forge.epita.fr`, so
|
||||
`exercises_shell` and any future `exercises_python` work with no change. The panel
|
||||
title is derived from the project segment of the node ids.
|
||||
|
||||
## If the numbers look wrong
|
||||
|
||||
The graph generator would have to have changed. Open the console on the page and
|
||||
run `__pgtDump()`: it prints the parsed nodes and the raw HTML of the first one,
|
||||
which is what `RE_STATE_ID` in `src/content.js` needs to match. `__pgtNet()` lists
|
||||
what the page fetched, in case richer data ever appears.
|
||||
Reference in New Issue
Block a user