Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,9 @@ jobs:
- name: Install dependencies
run: npm install

- name: Prove sulla resa dell'HTML
run: npm test

- name: Avvia il server
run: |
node server.js &
Expand Down
7 changes: 7 additions & 0 deletions desktop/main.js
Original file line number Diff line number Diff line change
Expand Up @@ -263,6 +263,13 @@ ipcMain.handle('compila', async (_e, documenti) => {
const f = path.join(lavoro, path.basename(nome));
return fs.existsSync(f) ? fs.readFileSync(f, 'utf-8') : null;
},
// Le eventuali immagini prodotte da tex4ht vivono nella cartella di
// lavoro, che cancelliamo subito dopo: vanno incorporate adesso o
// sono perse.
leggiBinario: (nome) => {
const f = path.join(lavoro, path.basename(nome));
return fs.existsSync(f) ? fs.readFileSync(f) : null;
},
quizCss: QUIZ_CSS,
quizJs: QUIZ_JS,
});
Expand Down
6 changes: 5 additions & 1 deletion latex-source/quiz.cfg
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,11 @@
% Uso: htlatex quiz.tex quiz.cfg
% Configurazione tex4ht per convertire quizstruct -> HTML interattivo

\Preamble{xhtml}
% mathml: senza quest'opzione tex4ht rende frazioni, integrali e radici come
% immagini PNG separate — sgranate, non selezionabili, e perse non appena il
% file HTML viene spostato. Con MathML le formule restano testo, che i browser
% impaginano da soli.
\Preamble{xhtml,mathml,charset="utf-8"}

% ------------------------------------------------------------
% \domanda[punti]{testo} -> <div class="question-block" data-punti="N" data-type="...">
Expand Down
227 changes: 206 additions & 21 deletions latex-source/quiz.css
Original file line number Diff line number Diff line change
@@ -1,4 +1,18 @@
/* quiz.css — output HTML del quiz */
/* quiz.css — output HTML del quiz
*
* Questo foglio deve reggere due sorgenti diverse: il markup pulito del
* generatore interno e quello di tex4ht, che avvolge il testo in strutture
* proprie (p.noindent, div.tabular, tabelle di impaginazione). Perciò oltre
* alle classi del quiz stila anche gli elementi generici del documento:
* altrimenti la parte discorsiva di una lezione esce senza impaginazione,
* larga quanto la finestra.
*
* I caratteri sono tutti di sistema. Il file HTML dev'essere autosufficiente
* e funzionare offline: non può scaricare font dalla rete, e incorporarli
* aggiungerebbe più di un megabyte a ogni compito. La prima voce di ogni
* elenco resta il carattere preferito, usato da chi ce l'ha installato; le
* altre sono presenti ovunque e scelte per somigliargli.
*/

:root {
--paper: #f6f5f0;
Expand All @@ -11,9 +25,28 @@
--wrong: #a13b2d; /* rosso: risposta sbagliata */
--wrong-bg: #f7e9e6;
--warn: #9a6700;
--mono: "IBM Plex Mono", "SF Mono", ui-monospace, Menlo, Consolas, monospace;
--sans: "IBM Plex Sans", ui-sans-serif, system-ui, sans-serif;
--serif: "Source Serif 4", Georgia, "Times New Roman", serif;

--mono: "IBM Plex Mono", ui-monospace, SFMono-Regular, "SF Mono", Menlo, Consolas, "Liberation Mono", monospace;
--sans: "IBM Plex Sans", system-ui, -apple-system, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif;
--serif: "Source Serif 4", "Iowan Old Style", Charter, Cambria, Georgia, "Times New Roman", serif;

/* Lunghezza di riga: sopra i ~75 caratteri l'occhio perde il capo riga. */
--misura: 42rem;
}

@media (prefers-color-scheme: dark) {
:root {
--paper: #12140f;
--paper-card: #1a1d17;
--ink: #e8e6dd;
--ink-dim: #9b9a91;
--line: #2f332a;
--accent: #9ec48f;
--accent-bg: #1f2a1c;
--wrong: #e2907f;
--wrong-bg: #2d1d19;
--warn: #d8b25a;
}
}

* { box-sizing: border-box; }
Expand All @@ -23,26 +56,100 @@ body {
background: var(--paper);
color: var(--ink);
font-family: var(--sans);
line-height: 1.5;
font-size: 1rem;
line-height: 1.6;
padding: 3rem 1.25rem 5rem;
-webkit-text-size-adjust: 100%;
}

/* ---- struttura del documento ----
* Una sola colonna centrata: vale sia per i blocchi del quiz sia per il testo
* che tex4ht sputa fuori come paragrafi sciolti.
*/

body > *,
.quiz,
.titolo {
max-width: var(--misura);
margin-left: auto;
margin-right: auto;
}

h1 {
max-width: 640px;
margin: 0 auto 0.25rem;
h1, h2, h3, h4 {
font-family: var(--serif);
font-weight: 600;
font-size: 2rem;
line-height: 1.25;
letter-spacing: -0.01em;
margin: 2.5rem auto 0.75rem;
}

.quiz {
max-width: 640px;
margin: 0 auto;
h1 { font-size: 2rem; margin-top: 0; }
h2 { font-size: 1.4rem; }
h3 { font-size: 1.15rem; }
h4 { font-size: 1rem; }

p {
font-family: var(--serif);
font-size: 1.05rem;
margin: 0 auto 1rem;
}

/* tex4ht marca i paragrafi come indent/noindent per imitare la stampa:
sul web il rientro di prima riga non serve, lo spazio verticale basta. */
p.indent, p.noindent { text-indent: 0; }

a { color: var(--accent); text-underline-offset: 2px; }

ul, ol { padding-left: 1.4rem; }
li { margin: 0.3rem auto; }

hr {
border: none;
border-top: 1px solid var(--line);
margin: 2.5rem auto;
}

code, tt, .verb {
font-family: var(--mono);
font-size: 0.92em;
}

/* Le tabelle di tex4ht escono spesso più larghe della colonna: invece di
sfondare la pagina, scorrono per conto loro. */
table {
border-collapse: collapse;
margin: 1.25rem auto;
max-width: 100%;
overflow-x: auto;
display: block;
width: fit-content;
}

td, th { padding: 0.35rem 0.7rem; text-align: left; }
th { font-family: var(--sans); font-size: 0.9rem; }

img { max-width: 100%; height: auto; }

/* ---- formule ----
* Con l'opzione mathml le formule sono testo vero. Una formula in blocco può
* comunque essere più larga dello schermo: la lasciamo scorrere invece di
* allargare la pagina.
*/
math { font-size: 1.05em; }

math[display="block"],
.math-display {
display: block;
overflow-x: auto;
overflow-y: hidden;
margin: 1.25rem auto;
padding-bottom: 0.25rem;
}

/* ---- intestazione del quiz ---- */

.quiz > h2 {
margin: 0 0 2rem;
margin: 0 auto 2rem;
padding-bottom: 0.75rem;
border-bottom: 1px solid var(--line);
font-family: var(--mono);
Expand All @@ -62,7 +169,8 @@ h1 {
border-left: 3px solid var(--ink);
border-radius: 3px;
padding: 1.25rem 1.5rem 1.5rem;
margin-bottom: 1.25rem;
margin: 0 auto 1.25rem;
max-width: var(--misura);
}

.question-block::before {
Expand All @@ -78,6 +186,9 @@ h1 {
padding: 0.1rem 0.45rem;
}

/* Senza data-punti il riquadro resterebbe vuoto e storto. */
.question-block:not([data-punti])::before { content: none; }

.question {
margin: 0 3.5rem 1.1rem 0;
font-family: var(--serif);
Expand All @@ -92,24 +203,33 @@ form.answers {
display: flex;
flex-direction: column;
gap: 0.15rem;
margin: 0;
}

label.option {
display: flex;
align-items: center;
align-items: baseline;
gap: 0.6rem;
padding: 0.4rem 0.5rem;
padding: 0.45rem 0.55rem;
border-radius: 3px;
cursor: pointer;
transition: background 0.12s ease;
}

label.option:hover { background: var(--accent-bg); }

/* Il focus da tastiera deve vedersi: si risponde anche senza mouse. */
label.option:focus-within {
outline: 2px solid var(--accent);
outline-offset: 1px;
}

label.option input[type="radio"] {
accent-color: var(--ink);
width: 16px;
height: 16px;
flex: none;
align-self: center;
}

label.option.correct {
Expand Down Expand Up @@ -143,7 +263,7 @@ select.tendina {
}

select.tendina:focus-visible {
outline: 2px solid var(--ink);
outline: 2px solid var(--accent);
outline-offset: 1px;
}

Expand Down Expand Up @@ -185,14 +305,33 @@ button {

button:hover { opacity: 0.85; }

button:focus-visible {
outline: 2px solid var(--accent);
outline-offset: 2px;
}

.quiz-controls {
display: flex;
flex-wrap: wrap;
gap: 0.6rem;
margin: 1.5rem auto 0;
max-width: var(--misura);
}

.calcola-btn {
display: block;
width: 100%;
flex: 1 1 14rem;
padding: 0.7rem;
font-size: 0.85rem;
letter-spacing: 0.02em;
}

.riprova-btn {
flex: 0 1 auto;
background: transparent;
color: var(--ink-dim);
border: 1px solid var(--line);
}

.feedback {
display: inline-block;
margin-left: 0.75rem;
Expand All @@ -208,7 +347,8 @@ button:hover { opacity: 0.85; }
/* ---- risultato finale ---- */

.risultato {
margin-top: 0.75rem;
margin: 0.75rem auto 0;
max-width: var(--misura);
padding: 1rem 1.25rem;
background: var(--ink);
color: var(--paper);
Expand All @@ -219,7 +359,52 @@ button:hover { opacity: 0.85; }
text-align: center;
}

/* ---- telefono ---- */

@media (max-width: 480px) {
body { padding: 2rem 1rem 4rem; }
body { padding: 2rem 1rem 4rem; font-size: 1.02rem; }
.question-block { padding: 1rem 1.1rem 1.25rem; }
.question { margin-right: 0; padding-top: 1.2rem; }
/* Con la colonna stretta il bollino dei punti finirebbe sopra il testo. */
.question-block::before { top: 0.9rem; right: 1.1rem; }
label.option { padding: 0.6rem 0.55rem; } /* dito, non puntatore */
}

/* ---- stampa ----
* L'HTML si stampa: quello che si stampa è il compito da svolgere, quindi
* senza i comandi interattivi né le correzioni già a video.
*/

@media print {
:root {
--paper: #fff;
--paper-card: #fff;
--ink: #000;
--ink-dim: #444;
--line: #999;
}

body { padding: 0; font-size: 11pt; }

button,
.quiz-controls,
.feedback,
.risultato { display: none !important; }

/* Le correzioni a video non devono finire sul foglio. */
label.option.correct,
label.option.wrong {
background: none;
color: inherit;
font-weight: normal;
text-decoration: none;
}

.question-block {
break-inside: avoid;
page-break-inside: avoid;
border-left-width: 1px;
}

a { color: inherit; text-decoration: none; }
}
3 changes: 2 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,8 @@
"build": "node build/build-app.mjs",
"lint": "echo 'No lint errors'",
"start": "node server.js",
"build:app": "node build/build-app.mjs"
"build:app": "node build/build-app.mjs",
"test": "node --test src/*.test.mjs"
},
"dependencies": {
"@pdf-lib/fontkit": "^1.1.1",
Expand Down
Loading
Loading