docs: reader + 3D atom hub design spec
This commit is contained in:
@@ -0,0 +1,129 @@
|
||||
# Design: formations reader + 3D atom hub (sub-project 1 of 4)
|
||||
|
||||
Date: 2026-06-10
|
||||
Status: approved (brainstorm with owner, visual companion session)
|
||||
|
||||
## Context
|
||||
|
||||
moodle.saillant.cc (Moodle 4.5 on Tower, restored 2026-06-10) hosts 6
|
||||
formations, each = 4 books (text-only HTML chapters) + 4 quizzes + forum +
|
||||
customcert certificate. 3 formations are published (esp32-ia, kicad-makers,
|
||||
freertos), 3 hidden (llm-locaux, iot-az, docker-selfhosting). The owner wants
|
||||
to replace the learner-facing Moodle UI with a web app matching the new
|
||||
electron 3D identity of lelectronrare.fr (dark + copper, canvas electron
|
||||
hero shipped 2026-06-10).
|
||||
|
||||
## Program decisions (brainstormed, apply to all 4 sub-projects)
|
||||
|
||||
1. **Full replacement**: the app becomes the entire learner experience;
|
||||
Moodle stays as backend (accounts, grades, completion, certificates)
|
||||
and as the author/admin tool.
|
||||
2. **Certificates**: generated by Moodle (customcert); the app celebrates
|
||||
and links to the PDF.
|
||||
3. **Access model**: published course content is readable WITHOUT an
|
||||
account (replaces "preview gratuite", gives SEO); a Moodle account is
|
||||
required for quizzes, saved progression, certificates.
|
||||
4. **Domain**: the app will ultimately TAKE moodle.saillant.cc; the Moodle
|
||||
UI moves to an admin subdomain (sub-project 4). Until then the app
|
||||
lives at formations.saillant.cc.
|
||||
5. **three.js role**: option B "the atom is the table of contents" — each
|
||||
course is a three.js atom: 4 orbits = 4 modules (books), electrons =
|
||||
chapters, click to read. Reading happens in a clean light panel.
|
||||
6. **Quizzes** (sub-project 3): rendered natively in the app; attempts and
|
||||
grades pushed to Moodle via its quiz API.
|
||||
|
||||
### Sub-projects
|
||||
|
||||
1. **Reader + 3D hub** (this spec) — public reading experience.
|
||||
2. Accounts + progression — login (Moodle token), progression lights up
|
||||
the atom's electrons.
|
||||
3. Native quizzes — in-app MCQ/true-false, graded via Moodle API.
|
||||
4. Certificates + domain swap — celebration screen, customcert PDF link,
|
||||
Moodle UI → lms-admin.saillant.cc, app takes moodle.saillant.cc.
|
||||
|
||||
## Sub-project 1 scope
|
||||
|
||||
### Stack & deployment
|
||||
|
||||
- **Astro 6 SSR (node adapter) + Tailwind 4** — same stack as
|
||||
lelectronrare.fr; one Docker container (node:22-alpine multi-stage, same
|
||||
Dockerfile pattern) on **Tower** next to Moodle, port **8096**.
|
||||
- Routing: traefik on electron-server (dynamic file `formations.yml`) →
|
||||
`http://100.78.6.122:8096` (Tailscale — the LAN 192.168.0.x is shadowed
|
||||
on ES by a docker bridge) + CF tunnel ingress `formations.saillant.cc`
|
||||
(API recipe, same as moodle/coder restores).
|
||||
- **three.js pinned** (exact version in package.json, lockfile committed;
|
||||
vendor/fork per the supply-chain HITL policy). It is loaded ONLY on
|
||||
course hub pages, lazily.
|
||||
- Repo: `electron-rare/formations-app` on Gitea (this repo).
|
||||
|
||||
### Data flow (BFF)
|
||||
|
||||
- The app's server (Astro API routes / loaders) is the only thing that
|
||||
talks to Moodle. Browser → app only.
|
||||
- Moodle **REST web services** with a dedicated read-only service token:
|
||||
`core_course_get_courses_by_field` (catalogue), `core_course_get_contents`
|
||||
(course structure: books, chapters), chapter HTML files fetched
|
||||
server-side with the token.
|
||||
- Admin prerequisite (done via Moodle CLI during implementation): enable
|
||||
web services + REST protocol, create service user + token with the
|
||||
minimal read functions.
|
||||
- **Cache**: in-memory per-process with TTL (content changes rarely) +
|
||||
stale-while-revalidate: if Moodle is down, serve the last good copy and
|
||||
log; only error pages if the cache is cold.
|
||||
- Slug mapping fixed in app config: `esp32-ia`, `kicad-makers`, `freertos`
|
||||
(+ hidden ones listed as "en préparation", never fetched publicly).
|
||||
|
||||
### Pages
|
||||
|
||||
- `/` — catalogue: 6 cards (3 open, 3 "en préparation"), dark + copper,
|
||||
consistent with lelectronrare.fr (reuse of the visual language, not the
|
||||
code).
|
||||
- `/cours/<slug>` — **the atom hub** (three.js): nucleus = the course; 4
|
||||
tilted orbits = the 4 modules (books); electrons on each orbit = that
|
||||
module's chapters. Hover an electron → chapter title tooltip; click →
|
||||
navigate to the chapter. Gentle orbital camera (drag to rotate, scroll
|
||||
zoom within limits). Module labels anchored to orbits. Progression
|
||||
rendering exists but all-grey in v1 (lights up in sub-project 2).
|
||||
- `/cours/<slug>/module-<n>/chapitre-<m>` — reading panel: light
|
||||
background, max-width prose column, book chapter HTML rendered
|
||||
**server-side** (free reading = indexable), breadcrumb, prev/next
|
||||
chapter, a small static atom position indicator in the margin (current
|
||||
electron highlighted), table of contents drawer.
|
||||
- Legacy URLs `/course/view.php?id=N` answered with 301 → `/cours/<slug>`
|
||||
(active from day one; becomes load-bearing at sub-project 4).
|
||||
|
||||
### Progressive enhancement & guard rails
|
||||
|
||||
- The hub page ALWAYS renders a complete HTML table of contents
|
||||
(modules + chapters list) server-side; the three.js atom replaces it
|
||||
visually when JS+WebGL are available. noscript, `prefers-reduced-motion`,
|
||||
WebGL-less, and low-end mobile users get the HTML sommaire. The 3D is an
|
||||
enhancement, never a wall.
|
||||
- Perf budget: three.js chunk lazy-loaded only on `/cours/<slug>`;
|
||||
reading pages ship no 3D; LCP < 2.5 s on reading pages; the atom scene
|
||||
pauses when the tab is hidden (same discipline as the lelectronrare
|
||||
hero: DPR cap, rAF pause, reduced-motion static).
|
||||
- Chapter HTML is sanitized server-side before render (defense in depth —
|
||||
authors are trusted today, but the pipe shouldn't assume it).
|
||||
|
||||
### Error handling
|
||||
|
||||
- Moodle WS unreachable: stale cache if warm; otherwise a friendly error
|
||||
page on course routes; `/` still renders (catalogue is app-config).
|
||||
- Unknown slug/module/chapter → 404 page (same dark identity).
|
||||
- three.js init failure → automatic fallback to the HTML sommaire
|
||||
(try/catch, no blank page).
|
||||
|
||||
### Testing
|
||||
|
||||
- `npm run build` green in Docker (node:22-alpine, --network=host on ES).
|
||||
- E2E curl: `/` 200, hub 200 with sommaire HTML present, chapter page 200
|
||||
with real book content, legacy `/course/view.php?id=2` → 301.
|
||||
- Manual: atom navigation on desktop + mobile, reduced-motion fallback,
|
||||
Lighthouse on a reading page.
|
||||
|
||||
## Out of scope (later sub-projects)
|
||||
|
||||
- Login, progression state, quiz rendering/grading, certificates,
|
||||
domain swap, Moodle admin relocation, payment.
|
||||
Reference in New Issue
Block a user