feat: migrate frontend React 18 → Svelte 5 + SvelteKit
All checks were successful
CI/CD — Build & Deploy / Build & Deploy (push) Successful in 22s

Core logic portable (economy, balance, cosmetics, migrateSave) — zero rewrite.
136 tests green, identiques. Backend inchangé.

- Svelte 5 runes stores (game, auth, toast) remplacent Zustand
- SvelteKit adapter-static SPA (dist/ output, fallback index.html)
- Tailwind v4 conservé, design system .gp-* porté
- Transitions natives : slide, fly, scale, fade sur toute l'UI
- Sidebar tabbée (Production/Evolution/Collection) + CollapsiblePanel
- Mobile bottom sheet avec FAB toggle + backdrop blur
- Click particles réactifs Svelte (plus de DOM impératif)
- TadpoleSprite bounce + glow ring au clic
- Guide refait en accordéon, Achievements avec filtres
- a11y : focus-visible, Escape modals, aria-current, aria-labels
- CI/CD adapté (tests + build + rsync)
- Build 504K (vs ~1.2MB React)
This commit is contained in:
2026-03-28 20:03:21 +01:00
parent 3de0492631
commit f6bff6e389
125 changed files with 5323 additions and 10373 deletions

View File

@@ -0,0 +1,64 @@
// auth.svelte.ts — Auth store (Svelte 5 runes)
// Cookie-based auth with SuperOAuth PKCE
import { apiFetch } from '$lib/api';
export interface User {
id: number;
nickname: string;
avatar_url?: string;
[key: string]: unknown;
}
let user = $state<User | null>(null);
let loading = $state(true);
async function refresh() {
try {
const data = await apiFetch('/auth/me');
user = data as User;
} catch {
user = null;
}
}
async function init() {
await refresh();
loading = false;
// Listen for expired session
if (typeof window !== 'undefined') {
window.addEventListener('auth:expired', () => {
user = null;
});
}
}
async function logout() {
try {
await apiFetch('/auth/logout', { method: 'POST' });
} catch {
// ignore
}
user = null;
}
async function editUser(updatedFields: Record<string, unknown>) {
if (!user) return;
const data = await apiFetch(`/users/${user.id}`, {
method: 'PUT',
body: JSON.stringify(updatedFields),
});
if (data?.user) {
user = { ...user, ...data.user };
}
}
export const authStore = {
get user() { return user; },
get loading() { return loading; },
init,
refresh,
logout,
editUser,
};