pwa works

This commit is contained in:
2026-06-14 23:38:06 +03:00
parent 7745bb2614
commit 933e56207e
8 changed files with 4998 additions and 88 deletions
+4 -2
View File
@@ -3,10 +3,12 @@
<head> <head>
<meta charset="UTF-8" /> <meta charset="UTF-8" />
<link rel="icon" type="image/svg+xml" href="/favicon.svg" /> <link rel="icon" type="image/svg+xml" href="/favicon.svg" />
<meta name="viewport" content="width=device-width, initial-scale=1.0, viewport-fit=cover" /> <link rel="apple-touch-icon" href="/icon-192.png" />
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no, viewport-fit=cover" />
<meta name="theme-color" content="#7c3aed" /> <meta name="theme-color" content="#7c3aed" />
<meta name="apple-mobile-web-app-capable" content="yes" /> <meta name="apple-mobile-web-app-capable" content="yes" />
<meta name="apple-mobile-web-app-status-bar-style" content="default" /> <meta name="apple-mobile-web-app-status-bar-style" content="black-translucent" />
<meta name="mobile-web-app-capable" content="yes" />
<title>JapLearn</title> <title>JapLearn</title>
</head> </head>
<body> <body>
+4929 -46
View File
File diff suppressed because it is too large Load Diff
+1
View File
@@ -15,6 +15,7 @@
}, },
"dependencies": { "dependencies": {
"@bokuweb/zstd-wasm": "^0.0.27", "@bokuweb/zstd-wasm": "^0.0.27",
"vite-plugin-pwa": "^1.3.0",
"wa-sqlite": "^1.0.0" "wa-sqlite": "^1.0.0"
} }
} }
Binary file not shown.

After

Width:  |  Height:  |  Size: 3.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.0 KiB

+5 -4
View File
@@ -1,11 +1,12 @@
{ {
"name": "Мое SQLite Приложение", "name": "JapLearn",
"short_name": "SQLite App", "short_name": "JapLearn",
"description": "Описание вашего приложения", "description": "Интервальные повторения японских слов",
"start_url": "/", "start_url": "/",
"display": "standalone", "display": "standalone",
"orientation": "portrait",
"background_color": "#ffffff", "background_color": "#ffffff",
"theme_color": "#000000", "theme_color": "#7c3aed",
"icons": [ "icons": [
{ {
"src": "/icon-192.png", "src": "/icon-192.png",
+29 -5
View File
@@ -36,13 +36,18 @@
* { * {
box-sizing: border-box; box-sizing: border-box;
-webkit-tap-highlight-color: transparent;
} }
body { body {
margin: 0; margin: 0;
min-height: 100dvh; min-height: 100dvh;
min-height: 100svh;
display: flex; display: flex;
flex-direction: column; flex-direction: column;
overscroll-behavior: none;
-webkit-overflow-scrolling: touch;
touch-action: manipulation;
} }
#app { #app {
@@ -53,10 +58,14 @@ body {
width: 100%; width: 100%;
margin: 0 auto; margin: 0 auto;
padding: 0; padding: 0;
padding-left: env(safe-area-inset-left);
padding-right: env(safe-area-inset-right);
padding-bottom: env(safe-area-inset-bottom);
} }
.app-header { .app-header {
padding: 1.5rem 1.25rem 0.5rem; padding: 1.5rem 1.25rem 0.5rem;
padding-top: calc(1.5rem + env(safe-area-inset-top));
text-align: center; text-align: center;
} }
@@ -134,9 +143,10 @@ body {
font-family: var(--sans); font-family: var(--sans);
font-size: 0.9rem; font-size: 0.9rem;
font-weight: 600; font-weight: 600;
padding: 0.65rem 1rem; padding: 0.75rem 1rem;
min-height: 48px;
border: 1px solid var(--border); border: 1px solid var(--border);
border-radius: 8px; border-radius: 10px;
background: var(--card-bg); background: var(--card-bg);
color: var(--text-h); color: var(--text-h);
cursor: pointer; cursor: pointer;
@@ -145,6 +155,9 @@ body {
align-items: center; align-items: center;
justify-content: center; justify-content: center;
gap: 0.4rem; gap: 0.4rem;
-webkit-tap-highlight-color: transparent;
touch-action: manipulation;
user-select: none;
} }
.btn:hover { .btn:hover {
@@ -154,6 +167,7 @@ body {
.btn:active { .btn:active {
transform: scale(0.97); transform: scale(0.97);
opacity: 0.9;
} }
.btn:disabled { .btn:disabled {
@@ -180,6 +194,7 @@ body {
background: var(--accent-light); background: var(--accent-light);
color: var(--text); color: var(--text);
margin-top: auto; margin-top: auto;
min-height: 36px;
} }
.status-bar.error { .status-bar.error {
@@ -194,6 +209,7 @@ body {
.app-footer { .app-footer {
padding: 1rem 1.25rem; padding: 1rem 1.25rem;
padding-bottom: calc(1rem + env(safe-area-inset-bottom));
text-align: center; text-align: center;
font-size: 0.75rem; font-size: 0.75rem;
color: var(--text); color: var(--text);
@@ -294,6 +310,7 @@ body {
position: sticky; position: sticky;
bottom: 0; bottom: 0;
z-index: 10; z-index: 10;
padding-bottom: env(safe-area-inset-bottom);
} }
.nav-btn { .nav-btn {
@@ -302,11 +319,14 @@ body {
font-size: 0.85rem; font-size: 0.85rem;
font-weight: 600; font-weight: 600;
padding: 0.75rem 0.5rem; padding: 0.75rem 0.5rem;
min-height: 52px;
border: none; border: none;
background: transparent; background: transparent;
color: var(--text); color: var(--text);
cursor: pointer; cursor: pointer;
transition: all 0.2s; transition: all 0.2s;
-webkit-tap-highlight-color: transparent;
touch-action: manipulation;
} }
.nav-btn.active { .nav-btn.active {
@@ -337,12 +357,14 @@ body {
} }
.sets-list li { .sets-list li {
padding: 12px 14px; padding: 14px 14px;
min-height: 48px;
cursor: pointer; cursor: pointer;
border-bottom: 1px solid var(--border); border-bottom: 1px solid var(--border);
transition: background 0.15s; transition: background 0.15s;
font-weight: 500; font-weight: 500;
color: var(--text-h); color: var(--text-h);
-webkit-tap-highlight-color: transparent;
} }
.sets-list li:last-child { .sets-list li:last-child {
@@ -379,7 +401,8 @@ body {
.sets-detail .mode-select { .sets-detail .mode-select {
width: 100%; width: 100%;
margin: 10px 0; margin: 10px 0;
padding: 8px 10px; padding: 10px;
min-height: 48px;
border: 1px solid var(--border); border: 1px solid var(--border);
border-radius: 8px; border-radius: 8px;
background: var(--bg); background: var(--bg);
@@ -465,6 +488,7 @@ body {
.vote-btn { .vote-btn {
font-size: 0.85rem; font-size: 0.85rem;
padding: 0.75rem 0.5rem; padding: 0.75rem 0.5rem;
min-height: 52px;
} }
.vote-easy { .vote-easy {
@@ -492,4 +516,4 @@ body {
.vote-ok { color: #facc15; border-color: #facc15; } .vote-ok { color: #facc15; border-color: #facc15; }
.vote-hard { color: #fb923c; border-color: #fb923c; } .vote-hard { color: #fb923c; border-color: #fb923c; }
.vote-none { color: #f87171; border-color: #f87171; } .vote-none { color: #f87171; border-color: #f87171; }
} }
+30 -31
View File
@@ -1,51 +1,49 @@
import { defineConfig } from 'vite'; import { defineConfig } from 'vite';
import basicSsl from '@vitejs/plugin-basic-ssl'; // npm i -D @vitejs/plugin-basic-ssl import basicSsl from '@vitejs/plugin-basic-ssl';
import { VitePWA } from 'vite-plugin-pwa'; import { VitePWA } from 'vite-plugin-pwa';
export default defineConfig({ export default defineConfig({
plugins: [basicSsl(), plugins: [
VitePWA({ // basicSsl(),
registerType: 'autoUpdate', // Автоматически обновлять SW при изменении кода VitePWA({
includeAssets: ['favicon.ico', 'apple-touch-icon.png', 'masked-icon.svg'], registerType: 'autoUpdate',
includeAssets: ['favicon.svg', 'icon-192.png', 'icon-512.png'],
manifest: { manifest: {
name: 'Мое PWA Приложение', name: 'JapLearn',
short_name: 'Моё PWA', short_name: 'JapLearn',
description: 'Описание моего приложения, работающего оффлайн', description: 'Интервальные повторения японских слов',
theme_color: '#ffffff', theme_color: '#7c3aed',
background_color: '#ffffff', background_color: '#ffffff',
display: 'standalone', // Важно для открытия без интерфейса браузера display: 'standalone',
orientation: 'portrait',
scope: '/', scope: '/',
start_url: '/', start_url: '/',
// Иконки обязательны для iOS и Android. Минимум 192x192 и 512x512
icons: [ icons: [
{ {
src: 'pwa-192x192.png', src: 'icon-192.png',
sizes: '192x192', sizes: '192x192',
type: 'image/png' type: 'image/png',
}, },
{ {
src: 'pwa-512x512.png', src: 'icon-512.png',
sizes: '512x512',
type: 'image/png'
},
{
src: 'pwa-512x512.png',
sizes: '512x512', sizes: '512x512',
type: 'image/png', type: 'image/png',
purpose: 'any maskable' // Для адаптивных иконок Android },
} {
] src: 'icon-512.png',
sizes: '512x512',
type: 'image/png',
purpose: 'any maskable',
},
],
}, },
workbox: { workbox: {
// Стратегия кэширования для оффлайн-работы globPatterns: ['**/*.{js,css,html,ico,png,svg,woff2,wasm}'],
globPatterns: ['**/*.{js,css,html,ico,png,svg,woff2}'],
// Если пользователь оффлайн и переходит по роуту, отдаем index.html
navigateFallback: '/index.html', navigateFallback: '/index.html',
// Игнорируем запросы к API или внешним ресурсам, если они не должны кэшироваться navigateFallbackDenylist: [/^\/api\//],
navigateFallbackDenylist: [/^\/api\//], },
} }),
}) ],
], // Добавит локальный HTTPS
build: { build: {
target: 'esnext', target: 'esnext',
@@ -59,6 +57,7 @@ export default defineConfig({
'Cross-Origin-Opener-Policy': 'same-origin', 'Cross-Origin-Opener-Policy': 'same-origin',
'Cross-Origin-Embedder-Policy': 'require-corp', 'Cross-Origin-Embedder-Policy': 'require-corp',
}, },
host: true host: true,
allowedHosts: ['jl.micialware.ru']
}, },
}); });