Gewijzigde functionaliteiten:
- Centreert doelvakken in letter-, reken-, klok-, geld-, plaatswaarde- en wereldwidgets.
- Houdt lange woordrijen scrollbaar zonder de eerste vakken af te snijden.
- Centreert kleinere geplaatste cijferblokken binnen hun doelvak.
- Verwijdert de links uitgelijnde compacte woordrij.
Tests: 160 geslaagd.
Gewijzigde functionaliteiten:
- Voegt vijf afwisselende avatardansen toe.
- Houdt lichaam, haar en accessoires als een verbonden SVG-figuur.
- Danst na afgeronde woordgroepen, flitsrondes, galgjerondes en rekenrondes.
- Respecteert verminderde beweging met een stil feestbeeld.
Tests: 159 geslaagd; npm audit: 0 kwetsbaarheden.
- Bug gevonden en bevestigd (niet blind aangenomen): math.js riep
onProgress maar op ÉÉN plek aan, in endRound(), NA een heel blok van
10 sommen, met alleen het geaggregeerde totaal. Eén sessie van 10
sommen - "een groep opdrachten" - leverde dus precies 1 rij op in
progress_events, niet 10, en maakte "welke som ging fout" onmogelijk
te zien (alleen een totaaltelling per blok).
- Fix: onProgress verplaatst naar check(), bij elke afgeronde som (goed
op de eerste keer, of opgeven na 3 pogingen) - een sessie van 10
sommen levert nu 10 progress_events-rijen op, elk met een nieuw
optioneel detail-veld (welke som, gegeven antwoord, juist antwoord).
letters.js/ball.js blijven deze ronde ongemoeid (hun granulariteit
veroorzaakte niet deze bug).
- Nieuwe kolom progress_events.detail (JSONB, nullable, puur additief -
db/020_progress_detail.sql). POST /my/progress accepteert het
optioneel; te groot/geen plain object wordt stilzwijgend genegeerd
zonder de rest van de log-poging te laten falen (detail is decoratief,
geen kernfunctionaliteit).
- Voortgang-tab (admin.js) herontworpen, zelfde ontwerptaal als de
eerdere School-navigator-herbouw: vakgebied-tabs, een
klassenmanagement-infobadge (Groep {n} · Niveau {n}, opgehaald via de
bestaande /levels/pupil/:id-route) zodat resultaten meteen tegen het
toegewezen niveau afgezet kunnen worden, een met de hand getekende
SVG-nauwkeurigheidsring + staafdiagram (goed/fout per dag, laatste 14
dagen - geen library, zelfde aanpak als avatar.js) en een "recente
fouten"-lijst die precies toont welke som fout ging. De bestaande
ruwe tijdlijn blijft eronder staan, niets is verwijderd.
- Terzijde gevonden en gefixt: .am-btn.active bestond nergens in de
CSS, waardoor de vakgebied-tabs (hier én in Klassenmanagement,
v0.4.34-beta) geen enkele visuele aanduiding hadden welke actief was.
- Tests: test/progress-logging.test.js uitgebreid met 4 tests voor het
detail-veld (opgeslagen, te groot genegeerd, geen plain object
genegeerd, afwezig blijft null). Volledige testsuite: 132/132.
- Live geverifieerd: een math.js-sessie met een mix van goede/foute
sommen doorlopen en bevestigd dat er 3 aparte POST /my/progress-
aanroepen gebeuren voor 3 sommen (niet 1 aggregaat), elk met het
juiste detail; de herbouwde Voortgang-tab geopend met gemockte data
en met screenshots bevestigd dat de nauwkeurigheidsring (38% = 3/8,
rekenkundig geverifieerd), het klassenmanagement-infobadge, de
staafdiagram en de "recente fouten"-lijst (met de daadwerkelijke som)
correct tonen, én dat de active-tab-fix ook op de Klassenmanagement-
tab zichtbaar is.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_014EPxzBVRXZZnBPvSAaPAbJ
- Grootste openstaande roadmap-punt. Nieuwe tabel pupil_levels
(db/019_pupil_levels.sql, zelfde class_id/pupil_id-vorm als
assignments) legt per vakgebied (taal/rekenen/world) een niveaugroep
vast (groep 1-8, gebaseerd op de leerjaren van de basisschool waaraan
de leerdoelen gekoppeld zijn) + niveau (1-3, fijnmazige verfijning
BINNEN die groep). Klas-standaard met individuele leerling-overrides,
net zo laagdrempelig instelbaar als de standaard - het speciaal-
onderwijs-geval waar dat vanaf het begin de norm is.
- Server: GET/PUT/DELETE /levels/class/:id en /levels/pupil/:id
(src/api.js) hergebruiken exact het assignments-patroon (upsert via
partial-unique-index, pupil-eerst-dan-klas-resolutie), plus
GET /my/level voor de leerling-kant. Zelfde permissie
(assignments.manage) en sameSchool/teacherOnly/pupilAccessible-
guards als de bestaande toewijzingsroutes.
- Nieuwe admin-tab "Klassenmanagement": klas + vakgebied kiezen, een
klas-standaard instellen, en per leerling de effectieve groep/niveau
zien (overerft van de klas tenzij een eigen afwijking is ingesteld)
met bewerk- en terug-naar-standaard-knoppen.
- ECHTE inhoudskoppeling, niet alleen een standaardwaarde: rekenen is
het eerste vakgebied waarvan de SLO/TULE-leerlijn direct in
getalgrenzen te vertalen is (groep 3 t/m 20, groep 4 t/m 100, groep 5
t/m 1000, groep 6 t/m 10.000). Nieuwe SLO_MATH_RANGES/mathRangeFor()
in data.js; math.js's gen() gebruikt dit voor de daadwerkelijke
getallen zodra een leerling een groep heeft (S.yearGroup), met het
bestaande niveau 1-3 als verfijning daarbinnen. Zonder groep blijft
het oude vaste 10/20/100-gedrag exact bestaan.
- pupil.js geeft groep/niveau door aan taal/rekenen-widgets bij het
mounten: niveau is een standaardwaarde (een al opgeslagen bord-niveau
blijft leidend), groep wordt voor rekenen-widgets altijd meegegeven
(er is geen "oud" groep-concept om te respecteren). Terzijde ook de
stille .catch() op de nieuwe async renderPupilWidgets() opgelost.
- Taal/wereldoriëntatie krijgen deze ronde nog geen eigen
inhoudskoppeling (vraagt een eigen contentbron per groep, nog niet
uitgezocht) - eerlijk benoemd als vervolgstap, geen loze belofte.
- Tests: test/pupil-levels.test.js (10 tests) dekt de server-routes
(mocked-pool-patroon) én - via dezelfde vm-techniek als elders in
test/widgets.test.js voor MONEY_LEVELS - een ECHTE (niet regex-)test
van mathRangeFor die bevestigt dat elke groep de juiste getalgrens
oplevert en niveau nooit boven de groep-grens uitkomt. Twee bugs
gevonden en gefixt tijdens het schrijven van deze tests: null-yearGroup
werd per ongeluk als groep 1 behandeld i.p.v. "geen groep", en de
testrol had geen klas-eigenaarschap waardoor 403 in plaats van 200
terugkwam. Volledige testsuite: 128/128.
- Live geverifieerd in de sandbox: klas-standaard + leerling-override
instellen in de nieuwe admin-tab, en drie scenario's in de
leerling-omgeving die bevestigen dat de daadwerkelijk gegenereerde
sommen binnen de juiste groep-grens blijven (geen klassenmanagement →
ongewijzigd oud gedrag; groep zonder eigen bord-niveau → volledige
groep-grens; groep mét een al opgeslagen bord-niveau → dat niveau
blijft leidend, geschaald naar de juiste groep-grens).
- public/js/data.js bevatte al niet-gerelateerde, nog niet gecommitte
wijzigingen van vóór deze sessie (extra taalthema's/woorden) - alleen
de SLO_MATH_RANGES/mathRangeFor-hunk is hier meegenomen (git add -p),
de rest blijft bewust ongemoeid staan, net als test/language-library.test.js.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_014EPxzBVRXZZnBPvSAaPAbJ
- Roadmap-punt "de voortgangen werken niet" onderzocht (niet blind
herbouwd - eerst gediagnosticeerd via de code en git-geschiedenis).
- Gevonden: pupil.js zette sinds v0.3.15-beta (toen kijken/werken-modus
werd toegevoegd) readonly:true in BEIDE takken van de modus-keuze -
een kopieerfout, want de hele reden voor die commit was net dat
"werken" de leerling wél moet laten werken. In "werken"-modus hoort
er geen readonly-vlag te staan; "kijken" (blijft ongewijzigd) is de
enige modus die dat nodig heeft.
- Concreet effect: de twee widgets die opts.readonly daadwerkelijk
lezen (letters.js/anchor.js) verborgen voor leerlingen in
werken-modus onterecht hun eigen bewerk-gereedschap (woordenlijst-
editor, opslaan/bibliotheek) - gereedschap dat een leerling in
werken-modus wél hoort te zien. De kernoefening zelf (spellen,
slepen) werd hierdoor niet geblokkeerd, dus dit verklaart niet de
volledige klacht - overige widgets negeren opts.readonly toch al.
- Tegelijk de stille `.catch(()=>{})` op het voortgang-loggen vervangen
door `console.warn` bij een mislukte POST /my/progress: een mislukte
log (verlopen sessie, weggehaalde toewijzing, servergolf) verdween
eerder spoorloos, wat een "werkt niet"-klacht onmogelijk te
onderzoeken maakte.
- Nieuwe test/progress-logging.test.js dekt het serverpad dat nog
helemaal geen tests had: leerling-eigen toewijzing wint van
klasbrede, thuiskind-zonder-school valt terug op de gekoppelde
ouder, geen-toewijzing geeft 400 zonder insert, niet-leerling geeft
403, ongeldige widgetId/widgetType geeft 400 - alle zes bevestigen
dat de SQL/toewijzingslogica zelf klopt, wat het probleem verder
vernauwt tot de nu twee gefixte punten hierboven of iets dat alleen
in de echte omgeving van de gebruiker reproduceerbaar is.
- Live geverifieerd: werken-modus toont nu het bewerk-gereedschap in
letters.js (voorheen verborgen), kijken-modus blijft exact
ongewijzigd (overlay + verborgen gereedschap). Volledige testsuite:
118/118.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_014EPxzBVRXZZnBPvSAaPAbJ
- De math.js-pilot (v0.4.23-beta: eigen personage kort naast "Goed!" bij
een juist antwoord) hergebruikt in vier andere widgets, zoals destijds
al voorzien in de roadmap: klok (mountClock), geld (mountMoney),
plaatswaarde (mountPlacevalue) en de wereldoriëntatie-spelopdracht
(mountWorldQuiz - dekt bomen/vogels/planten in één keer - en
mountTopography).
- Zelfde patroon overal: bij S.feedback==='good' (of S.spellSolved) en
een ingesteld personage verschijnt renderAvatarFace(...,"md") met de
av-cheer-animatie naast het bestaande feedbackbericht - puur visueel,
geen nieuw feedbacktype. Zonder personage blijft het gedrag exact
ongewijzigd.
- Geverifieerd met echte oplos-interacties (niet alleen mounten): klok
(typemodus + toetsenbord), geld (typemodus + toetsenbord), plaatswaarde
(blokken plaatsen via toetsenbord-Enter) en de spelopdracht (letters
plaatsen via toetsenbord-Enter) - in alle vier verschijnt het
personage daadwerkelijk in de feedback-HTML na een echt juist
antwoord. Volledige testsuite: 112/112.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_014EPxzBVRXZZnBPvSAaPAbJ
- Beide verzoeken hangen alleen af van dezelfde sessie-cookie, niet van
elkaars antwoord - ze werden tot nu toe na elkaar aangeroepen
(hydrateFromServer() startte pas na een voltooide /auth/me), wat een
hele round-trip extra kost bij elke opstart voor staf/thuisgebruikers.
- app.js vraagt ze nu tegelijk op via Promise.allSettled; hydrateFromServer()
krijgt het /me/data-resultaat optioneel meegegeven i.p.v. het zelf
(opnieuw) op te halen. Voor een leerling/ouder wordt dat resultaat
genegeerd (zij krijgen sowieso geen whiteboard-data) - nooit verspild
werk, soms ongebruikt, wat de server niet meer kost dan de bestaande
losse aanroep die er toch al was.
- De twee andere hydrateFromServer()-aanroepen (na inloggen, na een
save-conflict) geven bewust niets mee en halen gewoon vers op, zoals
voorheen - alleen de allereerste opstart kan dit parallelliseren.
- Geverifieerd met de volledige testsuite (112/112) en drie losse
boot-scenario's in de sandbox (gast/staf/leerling): in alle drie komen
/auth/me en /me/data binnen ~1ms van elkaar aan (voorheen serieel), en
blijft het bestaande gedrag (gast blijft gast, staf krijgt het bord,
leerling krijgt de afgeschermde weergave) exact hetzelfde.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_014EPxzBVRXZZnBPvSAaPAbJ
- "Exporteer als CSV"-knop toegevoegd aan het bestaande multireset-
resultatenscherm (klas-wachtwoorden resetten in de School-navigator),
naast de bestaande "kopieer alles"-knop.
- BELANGRIJKE BEPERKING, bewust zo: dit exporteert alleen wat op dit
scherm al staat (net-gegenereerde wachtwoorden). Wachtwoorden worden
na het zetten nergens leesbaar bewaard (alleen gehashet in de
database) - er is dus geen "haal wachtwoorden van bestaande accounts
op"-mogelijkheid en die komt er ook niet.
- CSV bevat gebruikersnaam (het echte inlogveld) naast de naam, ook al
toont het scherm zelf alleen de naam - zonder gebruikersnaam is een
uitdeelvel niet bruikbaar om mee in te loggen.
- Hergebruikt de csvValue()/downloadCsv()-helpers uit de vorige
resultaten-exportfeature (v0.4.29-beta) - eerste echte tweede
gebruiker, dus die keuze om ze lokaal in admin.js te houden i.p.v.
te verplaatsen was terecht.
- Geverifieerd met de volledige testsuite (112/112) en een live
browserdoorloop: klas-wachtwoorden resetten (dubbele bevestigingsklik)
→ CSV-download met correcte escaping van een leerlingnaam met komma
én aanhalingsteken.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_014EPxzBVRXZZnBPvSAaPAbJ
- Nieuwe sectie "Exporteren per klas" in de bestaande Voortgang-tab: klas
kiezen + "Exporteer als CSV" downloadt de volledige activiteitenlijst
van die klas (leerling, widget, pogingen, goed, sterren, datum/tijd).
- Bouwt voort op de bestaande route GET /progress/class/:id, die tot nu
toe geen enkele frontend-consument had.
- Server: nieuwe optionele ?limit=-queryparameter (begrensd op 5000) -
de export vraagt expliciet meer dan de standaard activiteitenweergave
(200) op, zodat een drukke klas niet stilzwijgend wordt afgekapt in het
exportbestand. Zonder de parameter blijft het bestaande gedrag (200)
ongewijzigd.
- Eerste CSV-export in deze codebase: kleine lokale csvValue()/
downloadCsv()-helpers in admin.js (komma/quote/newline-escaping,
BOM zodat Excel-NL accenten en komma-scheiding herkent) - bewust nog
niet gedeeld/verplaatst naar core.js, want er is nu nog maar één
gebruiker (de geplande wachtwoorden-export kan dit later hergebruiken).
- Tests: test/progress-export.test.js (limit wordt doorgegeven, te hoge
waarde wordt afgekapt op 5000, standaardgedrag zonder parameter blijft
200) + volledige testsuite (112/112). Ook live geverifieerd: klas
kiezen zonder selectie toont een hint i.p.v. te crashen, een echte
CSV-download met correcte escaping van komma's/aanhalingstekens in
een leerlingnaam, en de vertaalde widget-namen in de kolommen.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_014EPxzBVRXZZnBPvSAaPAbJ
- Nieuwe hoofdaccessoires: zonnebril, koptelefoon, feesthoedje.
- Nieuwe lichaamsaccessoires: ketting, rugtas (bandjes over de
schouders, zichtbaar van voren).
- Server-validatie (src/api.js) en vertalingen (nl+en) meegewerkt met
dezelfde nieuwe waarden - anders zou de client een optie tonen die de
server als "ongeldig hoofdaccessoire" afwijst.
- Geen wijziging aan de bestaande pet/kroon-onderdrukkingsregel nodig:
zonnebril/koptelefoon/feesthoedje bedekken het bovenhoofd niet volledig,
dus haar blijft er gewoon zichtbaar naast/onder.
- Geverifieerd: alle select-opties komen aan in de instellingen-editor,
een PUT met de nieuwe waarden slaagt (bestaande server-validatie
accepteert ze nu), en alle soort/accessoire-combinaties oogden goed in
screenshot (zonnebril op de dino, koptelefoon op het monster,
feesthoedje + ketting samen, rugtas op het dier).
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_014EPxzBVRXZZnBPvSAaPAbJ
- Dino: rugstekels stonden als een rij spitse vormen voor de hals/borst
("Ga als designer kijken" - gebruiker vond dit "wat vreemd"), wat als
een rare kraag oogde. Verplaatst naar een klein randje bovenop de kop
(zoals kleine hoorntjes) - dichter bij de anatomische plek (rug/kruin)
en rustiger op klein formaat.
- Dier (beer): had een los staartje opzij op dezelfde hoogte en met
vrijwel dezelfde vorm/grootte als de armen/benen, waardoor het als een
extra ledemaat oogde. Verwijderd - een beer laat van de voorkant
sowieso nauwelijks iets van zijn staart zien, dus dit is ook
anatomisch kloppender dan een zichtbaar zij-staartje.
- Overwogen: SVG vs. PNG-lagen als basis. SVG blijft de juiste keuze
hier - lichaams-/haarkleur zijn vrije kleurkiezers (elke hex-waarde),
wat via fill-attributen triviaal is maar bij PNG een vast palet of
canvas-hertintplugin zou vereisen; SVG schaalt bovendien scherp op
alle drie weergavegroottes zonder aparte assets.
- Geverifieerd: alle vijf bestaande scenario's, volledige testsuite,
en een School-navigator met een echte klas (leerling met dier-
personage toont nu geen vreemd zij-staartje meer).
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_014EPxzBVRXZZnBPvSAaPAbJ
- Kernprobleem: de CSS-laagjesversie (losse divs met percentage-posities
en z-index) had onderdelen die los van het lichaam hingen en een
tekenvolgorde die niet klopte (soort-specifieke delen soms achter
hoofd/lijf verstopt, soms er los voor zwevend). SVG lost dit
structureel op: tekenvolgorde = laagvolgorde, geen z-index nodig, en
elk onderdeel is een gebogen path/ellips die het silhouet van het
vorige onderdeel ruim overlapt in plaats van ernaast te zweven.
- Onderzocht hoe gangbare avatar-generators (DiceBear, Avataaars) dit
aanpakken: vaste 100x100 viewBox, onderdelen als SVG-groepen in de
juiste volgorde, kleur via directe fill-waarden i.p.v. CSS-variabelen
in SVG. Dat patroon hier toegepast, geen code overgenomen.
- Elke soort (dino/monster/dier) is nu een samenhangend lichaam: poten,
armen, staart/oren, buikvlek en gezicht overlappen allemaal duidelijk
met het hoofd/lijf. Dino: rugstekels, snuit met neusgaten, glimlach.
Monster: hoorntjes, brede mond met slagtandjes, asymmetrische ogen.
Dier: ronde oren, beer-snuit, kleine staart.
- Haar (spits/krullen/paardenstaart/mohawk) en accessoires (pet/kroon/
bril/strik/sjaal/cape/badge) opnieuw getekend zodat ze ook echt op
hun naam lijken, i.p.v. simpele gekleurde blokjes.
- Regel toegevoegd: een pet of kroon dekt het hele bovenhoofd af, dus
onderliggend haar wordt dan niet getekend (gaf anders rommelige,
half verstopte plukjes rond de rand) - een paardenstaart blijft wel
zichtbaar, want die hangt naar opzij.
- Kleuren (lichter voor buik/snuit, donkerder voor poten/staart/
rugstekels) worden nu berekend uit de gekozen lichaamskleur zelf
(avatarShade-helper), i.p.v. via CSS filter:brightness().
- CSS: alle oude .av-* laag-regels (~65 regels) verwijderd; alleen
afmetingen (sm/md/lg) en de bestaande instellingen/widget/
aanmoedigings-CSS blijven staan.
- Geverifieerd: alle vijf bestaande scenario's (opslaan, widget,
School-navigator-badge met een echte klas vol verschillende
personages, math.js-aanmoediging, ongeldige input), plus uitgebreide
screenshot-rondes van losse onderdelen, alle soort/haar/accessoire-
combinaties en alle drie weergavegroottes (24/44/180px) vóór het
committen.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_014EPxzBVRXZZnBPvSAaPAbJ
- Buikvlek (lichter gekleurde ovaal) op de romp - een klassiek dino-kenmerk
dat meteen de "kleurvlek zonder vorm"-indruk wegneemt.
- Twee kleine pootjes onderaan en twee stompe armpjes opzij (T-rex-stijl) -
het lichaam stond eerder als een rond blok zonder ledematen.
- Staart herzien naar een echte taps toelopende vorm (clip-path) i.p.v. de
ronde druppelvorm van hiervoor.
- Alleen voor soort "dino" - monster/dier ongewijzigd, geen regressie
(gecontroleerd via de screenshot-galerij van alle drie soorten samen).
- Geverifieerd op alle vijf bestaande scenario's (opslaan, widget,
School-navigator-badge, math.js-aanmoediging, ongeldige input) én
opnieuw op alle drie weergavegroottes (24/44/180px) vóór het committen.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_014EPxzBVRXZZnBPvSAaPAbJ
- Elk personage kreeg ogen (was volledig gezichtsloos - de kernoorzaak van de
lage beeldkwaliteit: een gekleurde vlek zonder gezicht oogt nooit als een
figuur, ongeacht soort/haar/accessoires).
- Dino kreeg een snuitje met neusgaten, een duidelijk zichtbaar staartje en
een derde, groter/donkerder rugstekel-plaatje - hiervoor waren de
rugstekels aanwezig maar onzichtbaar (z-index onder het lijf/hoofd, zie
vorige commit), nu bewust vóór het silhouet getekend zodat ze altijd tonen.
- Snuitje stond eerst z-index-onder de rugstekels (dus zelf ook onzichtbaar,
identieke fout als hierboven) - z-index verhoogd zodat het snuitje altijd
vóór de rugstekels ligt, wat ook anatomisch klopt (snuit zit vooraan).
- Geverifieerd met alle vijf scenario's uit de vorige verificatie (opslaan,
widget, School-navigator-badge, math.js-aanmoediging) plus een losse
screenshot-galerij op alle drie weergavegroottes (24/44/180px).
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_014EPxzBVRXZZnBPvSAaPAbJ
- Nieuwe migratie db/018_user_avatar.sql: kolom avatar_data (jsonb) op users.
- publicUser() geeft avatar overal automatisch mee (login, /auth/me,
/admin/users) - bestaande school/klas-rechten regelen vanzelf wie welke
personages ziet, geen nieuwe permissie-sleutel nodig.
- Nieuwe route PUT /me/avatar (open voor elke ingelogde rol, zelfde patroon
als /me/data): valideert soort/kleuren/haar/accessoires, slaat de eigen rij op.
- Nieuwe gedeelde module public/js/avatar.js: personage samenstellen uit
soort (dino/monster/dier - elk met een vast eigen lichaamsdeel, ook de
gevraagde dinostekels), lichaams- en haarkleur, haarstijl en hoofd-/
lichaamsaccessoires; gedeelde editor-UI voor zowel de instellingen-sectie
"Mijn personage" als de nieuwe widget.
- Instellingen-sectie "Mijn personage" (cms.js-patroon) - de leerling-toegang,
want widgets van categorie "class" zijn niet toewijsbaar in het
afgeschermde leerling-speelscherm.
- Widget 🦖 toegevoegd aan het bord (categorie "class") voor staf en
volledige-whiteboard-gebruikers.
- School-navigator (admin.js): leerkracht/beheerder ziet nu het personage
van elk klaslid in de personenlijst i.p.v. altijd de initialen-cirkel.
- Voorbereiding gerelateerd roadmappunt: math.js toont het eigen personage
kort naast "Goed!" als aanmoediging (pilot in één widget, geen nieuw
feedbacktype); zonder ingesteld personage blijft het gedrag ongewijzigd.
- Vertalingen (nl+en) voor de nieuwe widget, instellingen-sectie en editor.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_014EPxzBVRXZZnBPvSAaPAbJ
- Een schoolbeheerder (of systeemmanager) die aan een klas gekoppeld werd,
kwam daar niet zichtbaar te staan: de klas-detailweergave en de
staf-telling op de klastegel filterden op role==="teacher", terwijl de
server al sinds v0.3.38 elke stafrol (teacher/admin/super) toestaat in
class_teachers. Puur een weergavebug op de client - de koppeling zelf
werkte al goed. Nieuwe isClassStaff()-helper matcht nu exact de
serverkant se toegestane rollenset.
- De "+" in de A+-zoomknop (toolbar én het per-widget contentzoom-knopje)
gebruikte de CJK-volle-breedte "+" (U+FF0B) naast de gewone letter "A" -
dat teken valt vermoedelijk terug op een ander lettertype dan Quicksand,
waardoor de relatieve positie t.o.v. de "A" verspringt zodra
document.body.style.zoom verandert. Vervangen door de gewone ASCII "+"
(net als de A−-knop al een normaal minteken gebruikt, niet de volle-
breedte variant).
- Geluidsmeter: meet het klasgeluid live via de microfoon in een verticale
meter (rustig/pratend/te luid). Twee aanpasbare streefniveaus ("Rustig
tot" / "Te luid vanaf") laten de leerkracht de meter afstemmen op het
eigen lokaal en apparaat. Microfoon staat standaard uit en start alleen
na een expliciete klik; er wordt niets opgenomen of verstuurd, alles
blijft lokaal in de browser. Stopt de microfoonstream vanzelf zodra de
widget van het bord verdwijnt, ook als "Stop" niet eerst is ingedrukt.
- Gevoelszones: individuele check-in met vier zones (blauw/groen/geel/
rood, Zones of Regulation-stijl) - elke zone geeft een korte uitleg én
een concrete tip om jezelf te helpen. Anders dan de bestaande
Gevoelsmeter (mood.js), die een anonieme klaspoll is.
- Ademhalingsoefening: een groeiende/krimpende cirkel begeleidt rustig
ademhalen in drie instelbare tempo's (4-4, 4-7-8, box breathing),
met rondjesteller.
- Complimentenmachine: trekt een willekeurig compliment uit twintig
kindvriendelijke complimenten, met dezelfde spin-en-onthul-animatie als
de namenkiezer.
- Alle vier volledig tweetalig (nl/en), toegevoegd aan de mobiele-dekking-
test, en geverifieerd in de browser inclusief een echte (fake) micro-
foonstream voor de geluidsmeter.
- De nieuwe "Letterblokken"-werkvorm (v0.4.18) verklapte op elk niveau
altijd elke letter - alleen de soorten-/plaatsenpool werd moeilijker,
de spelopdracht zelf bleef identiek. Bij de originele letterblokken-
widget (letters.js) maakt niveau juist de opdracht zelf moeilijker:
niveau 1 verklapt alles, niveau 3 niets.
- Zelfde schaal nu ook hier: niveau 1 toont elke letter, niveau 2 ongeveer
de helft (willekeurig gekozen, vast per opdracht), niveau 3 toont geen
enkele letter. Het puntenpatroon per vak blijft op elk niveau zichtbaar
als tastbare hint, ook wanneer de letter zelf verborgen is.
- Geverifieerd: niveau 1 toont 11/11 letters, niveau 2 ~helft, niveau 3
0/13 (met de puntenpatronen nog intact).
- Bomen/vogels/planten/topografie verstopten "Niveau" als een los
keuzeveld onderin het inklapbare Instellingen-paneel; de oefenklok,
het geldwidget, plaatswaarde en rekenwidgets tonen niveau altijd als
een knoppenrij bovenaan, direct zichtbaar tijdens het spelen.
- Niveau is nu overal dezelfde knoppenrij (Niveau 1/2/3, met uitleg per
knop via title, net als bij de oefenklok) direct in de bovenbalk naast
de score - geen Instellingen meer nodig om niveau te wisselen.
- Niveau wisselen zet score en reeks terug naar 0, zoals bij de andere
oefenwidgets (voorheen bleef de score gewoon doorlopen).
- Topografie: de knoppenrij verdwijnt bij "Landsdelen", waar niveau toch
al niet van toepassing was - zelfde gedrag als voorheen, nu alleen
consistent met de rest.
- Bomen, vogels en wilde planten krijgen twee nieuwe werkvormen naast
Leren/Quiz: Flitskaarten (naam kort tonen, dan zelftoets - zelfde opzet
als het losse Flitswoorden-widget) en Letterblokken (de naam met
sleepblokken spellen, zelfde mechaniek als de letterblokken-widget,
inclusief puntenpatroon per letter).
- Topografie krijgt dezelfde twee werkvormen: flitskaarten tonen de kaart
met de plek pas gelabeld na onthullen, letterblokken laat de naam van de
gemarkeerde provincie/plaats/rivier/werelddeel spellen.
- Letterblokken werkt ook bij namen met spaties (bv. "Witte
paardenkastanje") - spaties worden een visuele tussenruimte, geen vak.
- Geverifieerd door alle vier widgets (bomen/vogels/planten/topografie)
volledig te spelen: flitskaart onthullen + zelftoets, en een woord
volledig spellen puur op basis van de getoonde puntenpatronen.
- De vorige puntenpatroon-fix (v0.4.16) loste alleen op dat de tien
cijferblokken onderling verschillen. De écht ontbrekende stap: bij de
letterblokken en de rekenwidgets (madd/msub/mmul/mdiv/mpct) toont een
leeg vakje altijd het puntenpatroon van het cijfer/de letter die er
hoort te staan - een tastbare hint, ongeacht wat er al ligt. Bij de
oefenklok en het geldwidget toonde het vakje in plaats daarvan niets
(leeg) of het patroon van wat er toevallig al in lag - geen hint.
- Oefenklok (blockAnswer), geldwidget (digitAnswer) en de nieuwe
plaatswaarde-oefening (buildAnswer) tonen nu overal het patroon van het
juiste doelcijfer per vak, consistent met letterblokken/rekenwidgets.
- Geverifieerd door drie oefeningen (geldrekenen niveau 2/3, oefenklok
niveau 3, plaatswaarde niveau 3) volledig op te lossen met uitsluitend
de puntenpatronen als leidraad (geen tekst gelezen) - in alle gevallen
"Goed gedaan!".
- De cijferblokken (oefenklok, geldwidget) hergebruikten de braille a-j-
patronen, die nooit de onderste rij (posities 3/6) gebruiken - daardoor
leken alle tien blokken op elkaar. Eigen DIGIT_DOTCODE-tabel die alle
zes stippen inzet, geeft elk cijfer nu een echt eigen vorm.
- Plaatswaardetabel-widget had geen enkele oefenvorm (alleen een vrij
invoerveld); nieuwe "Bouwen"-stand met drie niveaus (T/E, H/T/E, of
HD/TD/D/H/T/E) laat kinderen een getal opbouwen door cijferblokken in
de juiste kolom te slepen, met score/reeks en directe feedback - zelfde
bouwsteen als de oefenklok/het geldwidget. De vrije verkenstand blijft
ongewijzigd bestaan.
- Bijvangst: instellingenvelden met .hidden (bv. "24-uursklok" bij de
oefenklok) werden nooit echt verborgen door een CSS-specificiteitsbotsing
(.edu-controls label versloeg [hidden]); toegevoegde regel herstelt dit
voor alle leerwidgets.
- Nieuwe Cast-knop (📡) op de whiteboard-toolbar opent een sheet om het
geopende bord - of één widget erop met kijken/werken - direct naar een
klas of leerling uit te zenden, zonder om te weg via Instellingen.
- Hergebruikt de bestaande PUT/DELETE /assignments/class|pupil routes
ongewijzigd; geen serverwijziging nodig.
- Instellingen > Toewijzingen blijft bestaan maar toont nu alleen de
huidige toewijzing + een "Loskoppelen"-knop (aanmaken/wijzigen gebeurt
voortaan via Cast).