Initial commit: Zabavna Matematika web and Android app.
Kids math practice (SL/EN) in a Vite React app, wrapped with Capacitor for Android (net.bit76.matematika). Includes session refactor, i18n, and text-free difficulty icons with translated overlays.
|
After Width: | Height: | Size: 85 KiB |
|
After Width: | Height: | Size: 27 KiB |
|
After Width: | Height: | Size: 15 KiB |
|
After Width: | Height: | Size: 1.6 MiB |
@@ -0,0 +1,21 @@
|
||||
{
|
||||
"name": "Zabavna matematika",
|
||||
"short_name": "Matematika",
|
||||
"icons": [
|
||||
{
|
||||
"src": "/favicon/web-app-manifest-192x192.png",
|
||||
"sizes": "192x192",
|
||||
"type": "image/png",
|
||||
"purpose": "maskable"
|
||||
},
|
||||
{
|
||||
"src": "/favicon/web-app-manifest-512x512.png",
|
||||
"sizes": "512x512",
|
||||
"type": "image/png",
|
||||
"purpose": "maskable"
|
||||
}
|
||||
],
|
||||
"theme_color": "#ffffff",
|
||||
"background_color": "#ffffff",
|
||||
"display": "standalone"
|
||||
}
|
||||
|
After Width: | Height: | Size: 95 KiB |
|
After Width: | Height: | Size: 537 KiB |
|
After Width: | Height: | Size: 189 KiB |
|
After Width: | Height: | Size: 242 KiB |
|
After Width: | Height: | Size: 63 KiB |
|
After Width: | Height: | Size: 71 KiB |
|
After Width: | Height: | Size: 69 KiB |
|
After Width: | Height: | Size: 77 KiB |
|
After Width: | Height: | Size: 78 KiB |
|
After Width: | Height: | Size: 72 KiB |
|
After Width: | Height: | Size: 74 KiB |
|
After Width: | Height: | Size: 1.5 MiB |
|
After Width: | Height: | Size: 42 KiB |
|
After Width: | Height: | Size: 52 KiB |
|
After Width: | Height: | Size: 43 KiB |
|
After Width: | Height: | Size: 42 KiB |
|
After Width: | Height: | Size: 42 KiB |
|
After Width: | Height: | Size: 47 KiB |
|
After Width: | Height: | Size: 56 KiB |
|
After Width: | Height: | Size: 14 KiB |
@@ -0,0 +1,109 @@
|
||||
{
|
||||
"name": "Zabavna matematika",
|
||||
"short_name": "Matematika",
|
||||
"lang": "hr-HR",
|
||||
"description": "Interactive math games and exercises for kids.",
|
||||
"start_url": "./",
|
||||
"scope": "./",
|
||||
"display": "standalone",
|
||||
"orientation": "portrait-primary",
|
||||
"background_color": "#ffffff",
|
||||
"theme_color": "#ffffff",
|
||||
"icons": [
|
||||
{
|
||||
"src": "./favicon/web-app-manifest-192x192.png",
|
||||
"sizes": "192x192",
|
||||
"type": "image/png",
|
||||
"purpose": "any"
|
||||
},
|
||||
{
|
||||
"src": "./favicon/web-app-manifest-512x512.png",
|
||||
"sizes": "512x512",
|
||||
"type": "image/png",
|
||||
"purpose": "any"
|
||||
},
|
||||
{
|
||||
"src": "./favicon/apple-touch-icon.png",
|
||||
"sizes": "180x180",
|
||||
"type": "image/png",
|
||||
"purpose": "any"
|
||||
}
|
||||
],
|
||||
"screenshots": [
|
||||
{
|
||||
"src": "./screenshot/screenshot1.png",
|
||||
"sizes": "2048x2732",
|
||||
"type": "image/png",
|
||||
"label": "Level selection"
|
||||
},
|
||||
{
|
||||
"src": "./screenshot/screenshot2.png",
|
||||
"sizes": "2048x2732",
|
||||
"type": "image/png",
|
||||
"label": "Gameplay"
|
||||
},
|
||||
{
|
||||
"src": "./screenshot/screenshot3.png",
|
||||
"sizes": "2048x2732",
|
||||
"type": "image/png",
|
||||
"label": "Practice mode"
|
||||
},
|
||||
{
|
||||
"src": "./screenshot/screenshot4.png",
|
||||
"sizes": "1170x2532",
|
||||
"type": "image/png",
|
||||
"label": "Rewards"
|
||||
},
|
||||
{
|
||||
"src": "./screenshot/screenshot5.png",
|
||||
"sizes": "1170x2532",
|
||||
"type": "image/png",
|
||||
"label": "Settings"
|
||||
},
|
||||
{
|
||||
"src": "./screenshot/screenshot6.png",
|
||||
"sizes": "1824x2736",
|
||||
"type": "image/png",
|
||||
"label": "Achievements"
|
||||
},
|
||||
{
|
||||
"src": "./screenshot/screenshot7.png",
|
||||
"sizes": "1824x2736",
|
||||
"type": "image/png",
|
||||
"label": "More levels"
|
||||
}
|
||||
],
|
||||
"categories": ["education", "games"],
|
||||
"prefer_related_applications": false,
|
||||
"related_applications": [],
|
||||
"shortcuts": [
|
||||
{
|
||||
"name": "Play",
|
||||
"short_name": "Play",
|
||||
"description": "Start the main game",
|
||||
"url": "./?shortcut=play",
|
||||
"icons": [
|
||||
{
|
||||
"src": "./favicon/web-app-manifest-192x192.png",
|
||||
"sizes": "192x192",
|
||||
"type": "image/png",
|
||||
"purpose": "any"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "Practice",
|
||||
"short_name": "Practice",
|
||||
"description": "Open practice levels",
|
||||
"url": "./",
|
||||
"icons": [
|
||||
{
|
||||
"src": "./favicon/web-app-manifest-192x192.png",
|
||||
"sizes": "192x192",
|
||||
"type": "image/png",
|
||||
"purpose": "any"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
[
|
||||
"Math-is-Fun-001.mp3",
|
||||
"Math-is-Fun-002.mp3",
|
||||
"Math-is-Fun-003.mp3",
|
||||
"Math-is-Fun-004.mp3",
|
||||
"Math-is-Fun-005.mp3",
|
||||
"Math-is-Fun-006.mp3",
|
||||
"Math-is-Fun-007.mp3",
|
||||
"Math-is-Fun-008.mp3",
|
||||
"Math-is-Fun-009.mp3",
|
||||
"Math-is-Fun-010.mp3"
|
||||
]
|
||||
@@ -0,0 +1,61 @@
|
||||
<!doctype html>
|
||||
<html lang="sl">
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<meta name="viewport" content="width=device-width,initial-scale=1" />
|
||||
<title>Politika zasebnosti — Zabavna matematika</title>
|
||||
<meta name="description" content="Politika zasebnosti aplikacije Zabavna matematika (Slovenščina)." />
|
||||
<link rel="home" href="/" />
|
||||
<style>
|
||||
body{font-family:Segoe UI,Roboto,Arial,sans-serif;line-height:1.6;color:#111;background:#fff;padding:24px}
|
||||
main{max-width:780px;margin:0 auto}
|
||||
h1,h2{color:#0b69a3}
|
||||
footer{margin-top:36px;font-size:.9em;color:#555}
|
||||
a{color:#0b69a3}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<main>
|
||||
<h1>Politika zasebnosti</h1>
|
||||
<p><strong>Zabavna matematika</strong> ("aplikacija") spoštuje vašo zasebnost. Ta stran pojasnjuje, katere informacije zbiramo, kako jih uporabljamo in katere so vaše pravice. Dokument je v slovenščini in velja za aplikacijo in njeno spletno mesto.</p>
|
||||
|
||||
<h2>Kaj zbiramo</h2>
|
||||
<ul>
|
||||
<li><strong>Osebni podatki:</strong> Aplikacija ne zbira osebnih podatkov (npr. e-pošte, imen) razen če jih prostovoljno posredujete (npr. za podporo ali prijavo tekmovanja).</li>
|
||||
<li><strong>Podatki o uporabi:</strong> Samodejno lahko zbiramo anonimizirane podatke o tem, kako uporabljate aplikacijo (napake, zasloni, trajanje sej) za izboljšave in analitiko.</li>
|
||||
<li><strong>Piškotki in podobne tehnologije:</strong> Če aplikacija uporablja spletne strani, se lahko za analitiko uporabijo piškotki ali lokalna shramba.</li>
|
||||
</ul>
|
||||
|
||||
<h2>Kako uporabljamo podatke</h2>
|
||||
<p>Podatke uporabljamo za:</p>
|
||||
<ul>
|
||||
<li>izboljšanje delovanja in stabilnosti aplikacije,</li>
|
||||
<li>analizo uporabe in odpravljanje napak,</li>
|
||||
<li>prilagajanje izkušenj (če je omogočeno v nastavitvah).</li>
|
||||
</ul>
|
||||
|
||||
<h2>Sodelovanje s tretjimi ponudniki</h2>
|
||||
<p>Aplikacija lahko uporablja storitve tretjih oseb za analitiko, shranjevanje podatkov ali oglaševanje. Pogosto uporabljeni primeri so <em>Google Play Services</em>, <em>Firebase</em> in <em>Google Analytics</em>. Te storitve imajo svoje politike zasebnosti; prosimo, preverite njihove pogoje, če vas zanima, kako upravljajo podatke.</p>
|
||||
|
||||
<h2>Otroci</h2>
|
||||
<p>Ta aplikacija je namenjena otrokom. Ne zbiramo namensko osebnih podatkov otrok brez izrecnega soglasja staršev ali zakonitih skrbnikov. Če ste starš in menite, da so bili osebni podatki vašega otroka posredovani brez soglasja, nas kontaktirajte in ukrepali bomo.</p>
|
||||
|
||||
<h2>Varnost</h2>
|
||||
<p>Ukrepamo za zaščito zbranih informacij, vendar nobena metoda prenosa ali shranjevanja v spletu ni popolnoma varna. Ne moremo zagotoviti absolutne varnosti podatkov.</p>
|
||||
|
||||
<h2>Hramba podatkov</h2>
|
||||
<p>Podatke hranimo toliko časa, kot je potrebno za namene, opisane v tej politiki, razen če zakon zahteva drugače.</p>
|
||||
|
||||
<h2>Spremembe politike</h2>
|
||||
<p>To politiko lahko občasno posodobimo. Spremembe bodo objavljene na tej strani z navedbo veljavnega datuma.</p>
|
||||
|
||||
<h2>Kontakt</h2>
|
||||
<p>Za vprašanja glede zasebnosti nas kontaktirajte na: <a href="mailto:info@76bit.net">info@76bit.net</a>.</p>
|
||||
|
||||
<footer>
|
||||
<p>Uveljavljivost: 9. februar 2026</p>
|
||||
<p>Vrni se na <a href="/">domov</a>.</p>
|
||||
</footer>
|
||||
</main>
|
||||
</body>
|
||||
</html>
|
||||
|
After Width: | Height: | Size: 5.4 MiB |
|
After Width: | Height: | Size: 5.0 MiB |
|
After Width: | Height: | Size: 4.6 MiB |
|
After Width: | Height: | Size: 1.7 MiB |
|
After Width: | Height: | Size: 1.9 MiB |
|
After Width: | Height: | Size: 4.7 MiB |
|
After Width: | Height: | Size: 4.3 MiB |
@@ -0,0 +1,74 @@
|
||||
// Cache the app shell with paths relative to this worker's scope
|
||||
// so a subfolder deploy (Vite base: './') keeps working.
|
||||
const CACHE_NAME = 'matematika-shell-v3'
|
||||
|
||||
function scopeUrl(path) {
|
||||
return new URL(path, self.registration.scope).href
|
||||
}
|
||||
|
||||
const ASSETS_TO_CACHE = [
|
||||
'./',
|
||||
'./index.html',
|
||||
'./manifest.json',
|
||||
'./favicon/site.webmanifest',
|
||||
'./favicon/web-app-manifest-192x192.png',
|
||||
'./favicon/web-app-manifest-512x512.png',
|
||||
'./favicon/apple-touch-icon.png',
|
||||
'./favicon/favicon-96x96.png'
|
||||
].map(scopeUrl)
|
||||
|
||||
self.addEventListener('install', event => {
|
||||
event.waitUntil(
|
||||
caches.open(CACHE_NAME).then(cache =>
|
||||
cache.addAll(ASSETS_TO_CACHE).catch(err => {
|
||||
console.warn('Precache failed (continuing):', err)
|
||||
})
|
||||
)
|
||||
)
|
||||
self.skipWaiting()
|
||||
})
|
||||
|
||||
self.addEventListener('activate', event => {
|
||||
event.waitUntil(
|
||||
caches.keys().then(keys => Promise.all(
|
||||
keys.filter(k => k !== CACHE_NAME).map(k => caches.delete(k))
|
||||
))
|
||||
)
|
||||
self.clients.claim()
|
||||
})
|
||||
|
||||
self.addEventListener('fetch', event => {
|
||||
if (event.request.method !== 'GET') return
|
||||
|
||||
if (event.request.mode === 'navigate') {
|
||||
event.respondWith(
|
||||
fetch(event.request).then(resp => {
|
||||
if (resp && resp.status === 200) {
|
||||
const respClone = resp.clone()
|
||||
caches.open(CACHE_NAME).then(cache => cache.put(event.request, respClone))
|
||||
}
|
||||
return resp
|
||||
}).catch(() => caches.match(scopeUrl('./index.html')))
|
||||
)
|
||||
return
|
||||
}
|
||||
|
||||
event.respondWith(
|
||||
caches.match(event.request).then(cached => {
|
||||
const networkFetch = fetch(event.request).then(resp => {
|
||||
if (resp && resp.status === 200) {
|
||||
const respClone = resp.clone()
|
||||
caches.open(CACHE_NAME).then(cache => cache.put(event.request, respClone))
|
||||
}
|
||||
return resp
|
||||
}).catch(() => null)
|
||||
return cached || networkFetch
|
||||
})
|
||||
)
|
||||
})
|
||||
|
||||
self.addEventListener('message', event => {
|
||||
if (event.data && event.data.type === 'SKIP_WAITING') {
|
||||
self.skipWaiting()
|
||||
}
|
||||
})
|
||||