Verbeter anatomie en plaatsing van avataraccessoires (v0.4.46-beta) #1

Open
bes-r wants to merge 192 commits from bes-r/avatar-realism into main AGit
8 changed files with 555 additions and 246 deletions
Showing only changes of commit 4b54ea4a12 - Show all commits

View file

@ -1 +1 @@
0.2.22
0.3.00

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,4 @@
Quicksand - ontworpen door Andrew Paglinawan e.a.
Gelicenseerd onder de SIL Open Font License 1.1 (https://openfontlicense.org).
Bron: Google Fonts (https://fonts.google.com/specimen/Quicksand).
Dit bestand (quicksand.woff2) is het latin-subset van de variabele font (gewichten 300-700).

Binary file not shown.

48
public/img/logo.svg Normal file
View file

@ -0,0 +1,48 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 120 104" role="img" aria-label="teachus.today uil">
<!-- laag-poly uil: vlakke facetten in het merk-palet, transparante achtergrond -->
<!-- oren -->
<polygon points="33,28 40,10 48,22" fill="#1d3461"/>
<polygon points="62,22 70,10 77,28" fill="#1d3461"/>
<!-- voorhoofd -->
<polygon points="40,10 70,10 62,22 48,22" fill="#2456b8"/>
<!-- gezicht -->
<polygon points="33,28 48,22 44,38" fill="#2b4d94"/>
<polygon points="77,28 62,22 66,38" fill="#2b4d94"/>
<polygon points="48,22 62,22 66,38 44,38" fill="#24407a"/>
<!-- wangen -->
<polygon points="33,28 44,38 36,46" fill="#2f6fe4"/>
<polygon points="77,28 66,38 74,46" fill="#2f6fe4"/>
<!-- ogen -->
<circle cx="48.5" cy="30.5" r="4.6" fill="#cfe0f5"/>
<circle cx="61.5" cy="30.5" r="4.6" fill="#cfe0f5"/>
<circle cx="48.5" cy="30.5" r="2.3" fill="#101d3a"/>
<circle cx="61.5" cy="30.5" r="2.3" fill="#101d3a"/>
<!-- snavel -->
<polygon points="55,36 58.5,41 55,46 51.5,41" fill="#f28c38"/>
<!-- linkervleugel: cyaan/teal -->
<polygon points="36,46 44,38 44,56" fill="#3ac2e8"/>
<polygon points="36,46 44,56 32,62" fill="#18c5c0"/>
<polygon points="32,62 44,56 40,74" fill="#66d4ef"/>
<polygon points="32,62 40,74 30,78" fill="#12a8a4"/>
<polygon points="30,78 40,74 38,88" fill="#18c5c0"/>
<!-- borst: roze/oranje -->
<polygon points="44,38 55,46 44,56" fill="#e83e8c"/>
<polygon points="55,46 66,38 66,56" fill="#f28c38"/>
<polygon points="44,56 55,46 58,64" fill="#c2257f"/>
<polygon points="55,46 66,56 58,64" fill="#f06ba8"/>
<polygon points="44,56 58,64 48,78" fill="#f5a55c"/>
<polygon points="58,64 66,56 68,74" fill="#e83e8c"/>
<polygon points="48,78 58,64 68,74" fill="#8b5cf6"/>
<polygon points="48,78 68,74 58,86" fill="#f28c38"/>
<!-- rechterflank: blauw/navy -->
<polygon points="66,38 74,46 66,56" fill="#2f6fe4"/>
<polygon points="66,56 74,46 76,66" fill="#1d3461"/>
<polygon points="66,56 76,66 68,74" fill="#2456b8"/>
<polygon points="68,74 76,66 70,84" fill="#1d3461"/>
<!-- tak -->
<polygon points="26,92 106,85 106,88.4 26,95.4" fill="#1d3461"/>
<!-- blaadjes -->
<polygon points="92,70 99,78 92,84 85,78" fill="#18c5c0"/>
<polygon points="104,77 110,83 104,88 98,83" fill="#3ac2e8"/>
<polygon points="96,90 102,95 96,100 90,95" fill="#12a8a4"/>
</svg>

After

Width:  |  Height:  |  Size: 2.3 KiB

View file

@ -3,19 +3,19 @@
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>teach</title>
<link rel="icon" href="data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 64 64'%3E%3Crect width='64' height='64' rx='14' fill='%233b7dd8'/%3E%3Ctext x='32' y='46' font-size='40' font-weight='900' font-family='Arial Rounded MT Bold,Arial,sans-serif' text-anchor='middle' fill='%23fff'%3Et%3C/text%3E%3Ccircle cx='47' cy='17' r='7' fill='%23f7c948'/%3E%3C/svg%3E">
<title>teachus.today</title>
<link rel="icon" type="image/svg+xml" href="img/logo.svg">
<link rel="preload" href="fonts/quicksand.woff2" as="font" type="font/woff2" crossorigin>
<link rel="stylesheet" href="css/teach.css">
</head>
<body>
<div id="toolbar">
<div class="logo" title="teach">
<span class="l1">t</span><span class="l2">e</span><span class="l3">a</span><span class="l4">c</span><span class="l5">h</span>
<span class="crayon">🖍️</span>
<div class="logo" title="teachus.today">
<img class="logo-owl" src="img/logo.svg" alt="teachus.today">
<span class="logo-word"><span class="lw-teach">teach</span><span class="lw-us">us</span><span class="lw-dot">.</span><span class="lw-today">today</span></span>
</div>
<button class="tbtn ghost" id="btnMute">🔊</button>
<button class="tbtn ghost" id="btnHC">👁</button>
<button class="tbtn ghost" id="btnZoomOut">A</button>
<button class="tbtn ghost" id="zoomPct">100%</button>
<button class="tbtn ghost" id="btnZoomIn">A</button>
@ -23,10 +23,6 @@
<button class="tbtn ghost" id="btnFolders">📁</button>
<button class="tbtn ghost" id="btnSave">💾 <span data-i18n="saveBoard"></span></button>
<button class="tbtn ghost" id="btnUser">⚙️ <span id="userLbl"></span></button>
<div id="langToggle">
<button data-lang="nl">NL</button>
<button data-lang="en">EN</button>
</div>
<button class="tbtn ghost" id="btnFs" title="Fullscreen"></button>
</div>
@ -68,7 +64,10 @@
<div class="modalwrap" id="loginWrap">
<div class="modal" id="loginModal">
<h2 data-i18n="login"></h2>
<div class="login-logo">
<img class="logo-owl" src="img/logo.svg" alt="">
<span class="logo-word"><span class="lw-teach">teach</span><span class="lw-us">us</span><span class="lw-dot">.</span><span class="lw-today">today</span></span>
</div>
<div class="ed-tabs">
<button type="button" class="active" data-tab="login" data-i18n="tabLogin"></button>
<button type="button" data-tab="link" data-i18n="tabLinkCode"></button>
@ -95,6 +94,7 @@
</div>
</div>
<div class="guestnote" data-i18n="guestNote"></div>
<button class="tbtn ghost" type="button" id="btnGuestView" style="margin-top:10px;">🎨 <span data-i18n="stView"></span></button>
</div>
</div>
<div class="modalwrap" id="settingsWrap">
@ -106,6 +106,7 @@
<div class="ed-tabs" id="settingsTabs">
<button type="button" class="active" data-tab="account" data-i18n="stAccount"></button>
<button type="button" data-tab="view" data-i18n="stView"></button>
<button type="button" data-tab="admin" id="btnAdmin" style="display:none;" data-i18n="beheer"></button>
</div>
@ -137,6 +138,54 @@
</div>
</div>
<div id="settingsViewPanel" style="display:none;">
<div class="vw-row">
<span class="vw-lbl" data-i18n="vwTheme"></span>
<div class="seg" id="segTheme">
<button type="button" data-v="light" data-i18n="vwLight"></button>
<button type="button" data-v="dark" data-i18n="vwDark"></button>
<button type="button" data-v="auto" data-i18n="vwAuto"></button>
</div>
</div>
<div class="vw-row">
<span class="vw-lbl" data-i18n="vwContrast"></span>
<div class="seg" id="segHC">
<button type="button" data-v="off" data-i18n="vwOff"></button>
<button type="button" data-v="on" data-i18n="vwOn"></button>
</div>
</div>
<div class="vw-row">
<span class="vw-lbl" data-i18n="vwLang"></span>
<div class="seg" id="langToggle">
<button type="button" data-lang="nl">NL</button>
<button type="button" data-lang="en">EN</button>
</div>
</div>
<div class="vw-row">
<span class="vw-lbl" data-i18n="vwTbar"></span>
<div class="seg" id="segTbar">
<button type="button" data-v="top" data-i18n="vwTop"></button>
<button type="button" data-v="left" data-i18n="vwLeft"></button>
<button type="button" data-v="right" data-i18n="vwRight"></button>
</div>
</div>
<div class="vw-row">
<span class="vw-lbl" data-i18n="vwMotion"></span>
<div class="seg" id="segMotion">
<button type="button" data-v="on" data-i18n="vwOn"></button>
<button type="button" data-v="off" data-i18n="vwOff"></button>
</div>
</div>
<div class="vw-row">
<span class="vw-lbl" data-i18n="vwGlass"></span>
<div class="seg" id="segGlass">
<button type="button" data-v="on" data-i18n="vwOn"></button>
<button type="button" data-v="off" data-i18n="vwOff"></button>
</div>
</div>
<div class="guestnote" data-i18n="vwNote"></div>
</div>
<div id="settingsAdminPanel" style="display:none;">
<div id="adminModal"></div>
</div>

View file

@ -23,17 +23,59 @@ zoomPct.addEventListener("click", ()=>setZoom(100)); /* tap the % to reset */
document.addEventListener("langchange", ()=>{ btnZoomIn.title = btnZoomOut.title = zoomPct.title = T("zoomUI"); });
try{ const z = +localStorage.getItem("teach.zoom"); if(z>100) setZoom(z); else setZoom(100); }catch(e){ setZoom(100); }
/* high-contrast accessibility mode (device setting, persists) */
const btnHC = document.getElementById("btnHC");
function setHC(on){
document.body.classList.toggle("hc", on);
btnHC.classList.toggle("on", on);
btnHC.title = T("a11y");
try{ localStorage.setItem("teach.hc", on ? "1" : ""); }catch(e){}
/* weergave-instellingen (Instellingen Weergave): apparaat-gebonden voorkeuren.
Elke seg-groep: knopjes met data-v, keuze in localStorage, body-class toepassen. */
function initSeg(id, key, def, apply){
const seg = document.getElementById(id);
let val = def;
try{ val = localStorage.getItem(key) || def; }catch(e){}
if(![...seg.querySelectorAll("button")].some(b=>b.dataset.v===val)) val = def;
const set = v=>{
seg.querySelectorAll("button").forEach(b=>b.classList.toggle("active", b.dataset.v===v));
apply(v);
try{ localStorage.setItem(key, v); }catch(e){}
};
seg.querySelectorAll("button").forEach(b=>b.addEventListener("click", ()=>set(b.dataset.v)));
set(val);
}
btnHC.addEventListener("click", ()=>setHC(!document.body.classList.contains("hc")));
document.addEventListener("langchange", ()=>{ btnHC.title = T("a11y"); });
try{ if(localStorage.getItem("teach.hc")==="1") setHC(true); }catch(e){}
/* thema: licht / donker / automatisch (volgt het OS) */
const darkMq = window.matchMedia("(prefers-color-scheme: dark)");
const applyTheme = v=>{
document.body.classList.toggle("dark", v==="dark" || (v==="auto" && darkMq.matches));
};
initSeg("segTheme", "teach.theme", "light", applyTheme);
darkMq.addEventListener("change", ()=>{
try{ if((localStorage.getItem("teach.theme")||"light")==="auto") applyTheme("auto"); }catch(e){}
});
/* hoog contrast (voorheen de 👁-knop in de menubalk; zelfde localStorage-sleutel) */
(function(){
let init = "off";
try{ if(localStorage.getItem("teach.hc")==="1") init = "on"; }catch(e){}
const seg = document.getElementById("segHC");
const set = v=>{
seg.querySelectorAll("button").forEach(b=>b.classList.toggle("active", b.dataset.v===v));
document.body.classList.toggle("hc", v==="on");
try{ localStorage.setItem("teach.hc", v==="on" ? "1" : ""); }catch(e){}
};
seg.querySelectorAll("button").forEach(b=>b.addEventListener("click", ()=>set(b.dataset.v)));
set(init);
})();
/* menubalk-positie: boven / links / rechts */
initSeg("segTbar", "teach.tbar", "top", v=>{
document.body.classList.toggle("tb-left", v==="left");
document.body.classList.toggle("tb-right", v==="right");
});
/* minder beweging: standaard uit als het OS erom vraagt */
const motionDef = window.matchMedia("(prefers-reduced-motion: reduce)").matches ? "off" : "on";
initSeg("segMotion", "teach.motion", motionDef, v=>document.body.classList.toggle("no-motion", v==="off"));
/* glas-effect (transparantie/blur van widgets en balken) */
initSeg("segGlass", "teach.glass", "on", v=>document.body.classList.toggle("no-glass", v==="off"));
/* taalkeuze onthouden (de schakelaar staat nu onder Weergave) */
try{
const l = localStorage.getItem("teach.lang");
if(l && I18N[l]) LANG = l;
}catch(e){}
applyI18n();
updateEmptyHint();

View file

@ -2,7 +2,7 @@
"use strict";
/* version shown until /api/version resolves (or if the fetch fails, e.g. offline).
Kept in sync by hand with the VERSION file at the repo root on every release. */
const VERSION = "0.2.22";
const VERSION = "0.3.00";
(function(){
const tag = document.getElementById("verTag");
tag.textContent = "v"+VERSION;
@ -120,6 +120,11 @@ const I18N = {
login:"Inloggen", loginBtn:"Inloggen", registerBtn:"Account maken", logout:"Uitloggen",
settingsTitle:"Instellingen", tabLogin:"Inloggen", tabLinkCode:"Koppelcode", busy:"Bezig…",
stAccount:"Account", stPassword:"Wachtwoord", stSession:"Sessie",
stView:"Weergave", vwTheme:"Thema", vwLight:"Licht", vwDark:"Donker", vwAuto:"Auto",
vwContrast:"Hoog contrast", vwOn:"Aan", vwOff:"Uit", vwLang:"Taal",
vwTbar:"Menubalk", vwTop:"Boven", vwLeft:"Links", vwRight:"Rechts",
vwMotion:"Animaties", vwGlass:"Glas-effect",
vwNote:"Deze voorkeuren gelden voor dit apparaat.",
pwChanged:"Wachtwoord gewijzigd ✓",
namePh:"naam", passPh:"wachtwoord",
guestNote:"Zonder inloggen wordt niets bewaard. Log in met je schoolaccount om alles op de server te bewaren.",
@ -254,6 +259,11 @@ const I18N = {
login:"Log in", loginBtn:"Log in", registerBtn:"Create account", logout:"Log out",
settingsTitle:"Settings", tabLogin:"Log in", tabLinkCode:"Link code", busy:"Please wait…",
stAccount:"Account", stPassword:"Password", stSession:"Session",
stView:"Display", vwTheme:"Theme", vwLight:"Light", vwDark:"Dark", vwAuto:"Auto",
vwContrast:"High contrast", vwOn:"On", vwOff:"Off", vwLang:"Language",
vwTbar:"Menu bar", vwTop:"Top", vwLeft:"Left", vwRight:"Right",
vwMotion:"Animations", vwGlass:"Glass effect",
vwNote:"These preferences apply to this device.",
pwChanged:"Password changed ✓",
namePh:"name", passPh:"password",
guestNote:"Without logging in nothing is saved. Log in with your school account to keep everything on the server.",
@ -321,7 +331,11 @@ function applyI18n(){
document.dispatchEvent(new CustomEvent("langchange"));
}
document.querySelectorAll("#langToggle button").forEach(b=>{
b.addEventListener("click", ()=>{ LANG=b.dataset.lang; applyI18n(); });
b.addEventListener("click", ()=>{
LANG=b.dataset.lang;
try{ localStorage.setItem("teach.lang", LANG); }catch(e){}
applyI18n();
});
});
document.getElementById("btnFs").addEventListener("click", ()=>{
if(document.fullscreenElement) document.exitFullscreen(); else document.documentElement.requestFullscreen();
@ -427,14 +441,16 @@ function switchLoginTab(tab){
document.querySelectorAll("#loginModal .ed-tabs button").forEach(b=>{
b.addEventListener("click", ()=>switchLoginTab(b.dataset.tab));
});
/* instellingen-tabs: "account" vs "beheer" - alles in één overzicht i.p.v. een los venster */
/* instellingen-tabs: "account" / "weergave" / "beheer" - alles in één overzicht */
function switchSettingsTab(tab){
document.querySelectorAll("#settingsTabs button").forEach(b=>b.classList.toggle("active", b.dataset.tab===tab));
document.getElementById("settingsAccountPanel").style.display = tab==="account" ? "block" : "none";
document.getElementById("settingsViewPanel").style.display = tab==="view" ? "block" : "none";
document.getElementById("settingsAdminPanel").style.display = tab==="admin" ? "block" : "none";
}
/* het "beheer"-tabblad heeft zijn eigen klik-handler in admin.js (moet eerst data laden) */
document.querySelector('#settingsTabs button[data-tab="account"]').addEventListener("click", ()=>switchSettingsTab("account"));
document.querySelector('#settingsTabs button[data-tab="view"]').addEventListener("click", ()=>switchSettingsTab("view"));
async function loadSchools(){
const sel = document.getElementById("liSchool");
try{
@ -445,7 +461,10 @@ async function loadSchools(){
}
function openSettings(){
settingsMsg.textContent = ""; settingsMsg.classList.remove("ok");
switchSettingsTab("account");
/* zonder account is er geen Account-tab: dan direct naar Weergave
(zo blijven taal/contrast/thema ook als gast bereikbaar) */
document.querySelector('#settingsTabs button[data-tab="account"]').style.display = currentUser ? "" : "none";
switchSettingsTab(currentUser ? "account" : "view");
settingsWrap.classList.add("open");
}
document.getElementById("btnUser").addEventListener("click", ()=>{
@ -455,6 +474,11 @@ document.getElementById("btnUser").addEventListener("click", ()=>{
loadSchools();
loginWrap.classList.add("open");
});
/* gast-ingang naar de weergave-instellingen vanuit het inlogscherm */
document.getElementById("btnGuestView").addEventListener("click", ()=>{
loginWrap.classList.remove("open");
openSettings();
});
document.getElementById("btnSettingsClose").addEventListener("click", ()=>settingsWrap.classList.remove("open"));
loginWrap.addEventListener("click", e=>{ if(e.target===loginWrap) loginWrap.classList.remove("open"); });
settingsWrap.addEventListener("click", e=>{ if(e.target===settingsWrap) settingsWrap.classList.remove("open"); });