Skip to content

feat(percorsi): percorsi personalizzati — libro unico + codice deterministico - #14

Merged
marcofarina merged 7 commits into
mainfrom
feat/percorsi-personalizzati
Jul 18, 2026
Merged

feat(percorsi): percorsi personalizzati — libro unico + codice deterministico#14
marcofarina merged 7 commits into
mainfrom
feat/percorsi-personalizzati

Conversation

@marcofarina

Copy link
Copy Markdown
Owner

Summary

  • Sostituisce le sidebar multiple IT/Liceo/ITS con un libro unico per volume + una pagina /percorso dove il docente seleziona lezioni/capitoli e ottiene un codice deterministico (PDB1-…) da distribuire agli studenti.
  • Zero backend: manifest a epoche (append-only nell'epoca corrente, compattazione annuale via npm run curriculum:new-epoch), payload RLE/bitmask Crockford-base32 con CRC-8, guardia CI append-only (npm run curriculum:check).
  • Rimosso il vecchio sistema (PathContext, pathLabels, PathSelector, OffPathBanner).

Fasi (design chiuso in pm/design-percorsi-personalizzati.md, milestone m9)

  1. Data layer — curriculum/toc/*.js, manifest.json a epoche, codec src/lib/curriculumCode.ts, plugins/curriculum
  2. Collasso sidebar + CurriculumContext (localStorage, ?percorso= da URL, SSR-safe)
  3. Filtro — curriculumFilter.ts, DocRoot, DocPaginator, OffCurriculumBanner
  4. Configuratore /percorso (checkbox tri-state, preset IT/Liceo/ITS, copia codice/link, QR per la LIM), CurriculumIndicator in navbar, rimozione vecchio sistema
  5. Polish — guardia CI append-only, script apertura epoca, aggiornamento CLAUDE.md/skill nuovo-capitolo

Include anche il fix critical su websocket-driver (override in package.json, commit ae05f61).

Bump versione: 0.12.0 → 0.13.0 (feature multi-fase).

Test plan

  • npm run build passa (manifest completo, nessuna lezione mancante)
  • npm run curriculum:check passa in CI (test-deploy.yml)
  • Round-trip codec verificato (encode/decode, corruzione CRC rifiutata)
  • E2E manuale in browser: configuratore, preset, copia codice/link, QR, attivazione studente via ?percorso=, sidebar/paginatore filtrati, banner fuori percorso, chip navbar
  • Revisione finale di Marco prima del merge in main

🤖 Generated with Claude Code

marcofarina and others added 7 commits July 16, 2026 15:58
…bitmask, plugin curriculum (fase 1)

Fase 1 del design pm/design-percorsi-personalizzati.md (milestone m9):

- curriculum/toc/<volume>.js: albero sidebar unico per volume (unione dei
  percorsi it/liceo/its), fonte unica per sidebar e manifest
- curriculum/presets.js: IT/Liceo/ITS come selezioni curate
- curriculum/manifest.json: posizioni di bit per epoca; epoca corrente
  append-only, epoche passate immutabili (i codici non scadono mai)
- src/lib/curriculumCode.ts: codec puro PDB1-… (Crockford base32, CRC-8,
  varint LEB128); l'encoder emette il più corto tra bitmask e RLE
- plugins/curriculum: walk TOC + titoli dal frontmatter, append automatico
  al manifest in dev, build prod fallisce se il manifest è incompleto,
  validazione preset, setGlobalData per il client

Verifica: round-trip esaustivo del codec (N≤10 completo + random N=65535),
CRC intercetta 496/496 typo a singolo carattere, build prod verde e rossa
come atteso rimuovendo un'entry dal manifest.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_019y4SRapHyeCQ2EGaNxQeas
- sidebars/<volume>.ts: una sola sidebar `libro` che require() l'albero
  condiviso da curriculum/toc/ (assunzione CJS-in-TS confermata dalla build).
  Il vecchio sistema degrada a no-op da solo: PathSelector si nasconde con
  ≤1 sidebar, DocRoot ricade sul default se la sidebar salvata non esiste.
- src/contexts/CurriculumContext.tsx: codice canonico in localStorage
  `pdb:curriculum`, idratazione a due passate SSR-safe, sync cross-tab,
  decodifica memoizzata contro il manifest a epoche → ReadonlySet delle
  chiavi incluse; `?percorso=CODE` viene validato, normalizzato, persistito
  e rimosso dall'URL (history.replaceState).
- Root.tsx: CurriculumProvider (PathProvider resta fino alla fase 4).
- curriculumCode.ts: reason `epoch` per i codici di epoche future.

Verifica sul build servito: sidebar unica completa, PathSelector sparito,
?percorso= accetta anche codici "sporchi" (minuscole, spazi, O/I per 0/1)
e li canonicalizza; codice invalido → warn senza persistenza né crash;
nessun warning di idratazione in console.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_019y4SRapHyeCQ2EGaNxQeas
…fase 3)

- src/lib/curriculumFilter.ts: filterSidebarItems (ricorsivo, elimina le
  categorie svuotate, preserva link non-doc) + flattenSidebarLinks per la
  paginazione (categoria con href prima dei figli, come il nativo).
- DocRoot: con percorso attivo filtra la PropSidebar del volume corrente;
  SSR-safe (primo render = included null = libro intero). Via la logica
  PathContext a sidebar multiple.
- DocPaginator (wrapper di @theme-original): senza percorso passthrough;
  con percorso ricalcola prev/next dalla sidebar già filtrata; doc fuori
  percorso → nessun paginator.
- OffCurriculumBanner (sostituisce OffPathBanner in DocItem/Content):
  lezione esclusa aperta via link diretto → contenuto leggibile + avviso
  con «Vedi tutto il libro» (clear live) e «Gestisci percorso» (anchor
  semplice finché /percorso non esiste, fase 4).

Verifica E2E sul build servito: la sidebar nasconde la lezione esclusa,
prev/next la salta (perché-gli-oggetti → metodi-di-classe saltando
classi-e-istanze), l'URL diretto mostra contenuto + banner senza paginator,
«Vedi tutto il libro» ripristina sidebar/paginator/banner live senza
navigare e pulisce localStorage.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_019y4SRapHyeCQ2EGaNxQeas
… vecchio sistema (fase 4)

- src/pages/percorso.tsx (+ css module): pagina a doppio ruolo.
  Studente: campo codice + Attiva, errori mirati per reason (crc/epoch/…),
  stato attivo con targhetta, «Inizia a studiare», «Rimuovi percorso».
  Docente: albero volume → capitolo (tri-state) → lezione con le
  sempre-visibili spuntate e disabilitate, preset IT/Liceo/ITS + «Libro
  intero» + «Importa il codice attivo», targhetta live sticky con conteggio
  lezioni, «Copia codice» e «Copia link» (?percorso=CODE).
- src/lib/curriculumSelection.ts: codeFromKeys/keysFromCode condivisi tra
  context, pagina e indicatore (il context ora valida via keysFromCode).
- CurriculumIndicator: chip navbar (slot center) con label del preset se il
  codice canonico coincide (confronto stringa), altrimenti «Percorso
  attivo»; link a /percorso, «×» per rimuovere. Sostituisce PathSelector.
- OffCurriculumBanner: «Gestisci percorso» ora è un <Link> vero.
- Rimossi PathContext, pathLabels, PathSelector, OffPathBanner;
  Root.tsx resta col solo CurriculumProvider.

Verifica: typecheck/lint/build verdi; SSR di /percorso corretto (header,
card studente, albero completo, targhetta con il codice canonico identico
a quello del test Node del codec). Interattività della pagina (tri-state,
copia, preset) da verificare a mano nel browser: l'estensione Chrome si è
disconnessa durante l'E2E.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_019y4SRapHyeCQ2EGaNxQeas
…ebsocket-driver

- «Mostra QR» nel pannello codice di /percorso: overlay a tutto schermo
  pensato per la proiezione in classe — targa bianca (contrasto e quiet
  zone anche in dark), QR grande del link condiviso (?percorso=CODE) e
  codice in chiaro sotto per chi digita. Libreria `qrcode` (SVG generato
  client-side, nessuna risorsa esterna). Chiusura con Esc, «×» o click
  fuori; scroll-lock e reduced-motion rispettati.
- Security: override "websocket-driver": "^0.7.5" per la nuova advisory
  critical GHSA-mp7j-qc5w-4988 (catena webpack-dev-server → sockjs, solo
  dev-server). npm audit torna a 0 critical (48 residue, dev/build-time).

Verifica in browser sul build servito: overlay apre con QR SVG e codice,
Esc chiude e ripristina lo scroll; typecheck/lint/build verdi.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_019y4SRapHyeCQ2EGaNxQeas
…se 5)

- scripts/check-manifest-append-only.js (npm run curriculum:check):
  confronta curriculum/manifest.json con origin/main — epoche passate
  immutabili, epoca corrente append-only (prefisso), currentEpoch può solo
  restare o avanzare di 1 con la nuova lista presente. Ref senza manifest →
  check saltato. Cablato in test-deploy.yml prima del lint.
- scripts/curriculum-new-epoch.js (npm run curriculum:new-epoch): rito
  annuale — rigenera l'elenco dalla TOC in ordine curricolare senza
  tombstone come epoca N+1, congela la precedente (i codici distribuiti
  restano validi); no-op con avviso se l'epoca corrente coincide già con
  la TOC (--force per forzare).
- plugins/curriculum: esporta flattenTocDocIds per lo script.

Testati entrambi: guardia OK su identico e append, VIOLAZIONE su riordino;
new-epoch no-op su manifest allineato, apertura epoca 2 con tombstone
eliminato e epoca 1 congelata. Aggiornati anche CLAUDE.md e lo skill
nuovo-capitolo (non tracciati: .claude/ e CLAUDE.md sono gitignorati) —
le lezioni nuove si aggiungono in curriculum/toc/<volume>.js.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_019y4SRapHyeCQ2EGaNxQeas
Milestone m9 completa: tutte le 5 fasi (data layer, sidebar unica +
CurriculumContext, filtro, configuratore, guardia CI append-only).

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
@marcofarina
marcofarina merged commit 31fcbed into main Jul 18, 2026
1 check passed
@marcofarina
marcofarina deleted the feat/percorsi-personalizzati branch July 18, 2026 23:35
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant