OpenMind v1 : cartes mentales libres, local-first et dockerisees
Premiere version fonctionnelle, demarrable avec `docker compose up -d --build` sur http://localhost:8080. Architecture - Frontend React 18 / TypeScript strict / Vite / Tailwind, canvas @xyflow/react, etat Zustand, persistance IndexedDB via Dexie, auto-layout dagre. - Backend FastAPI / SQLAlchemy 2 / Pydantic v2 / SQLite, qui sert aussi les fichiers statiques du frontend compile. - Image Docker multi-stage, un seul service, bind mount ./data pour SQLite. Local-first Les cartes restent dans le navigateur. Le serveur ne recoit une carte que sur demande explicite de partage. Partage Deux secrets distincts : share_token public en lecture seule dans l'URL, et management_token conserve uniquement cote client, transmis en en-tete pour les modifications et stocke hashe en base. Fonctionnalites Tableau de bord, editeur complet (undo/redo, copier/coller, raccourcis, branches repliables, recherche), 9 themes, 4 dispositions automatiques, panneau de proprietes, import/export .mmap, export PNG/SVG/PDF, impression, page publique en lecture seule, PWA. Qualite 25 tests pytest et 43 tests vitest au vert, ruff et ESLint sans erreur, TypeScript strict sans erreur. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
41
frontend/src/stores/appConfigStore.ts
Normal file
41
frontend/src/stores/appConfigStore.ts
Normal file
@@ -0,0 +1,41 @@
|
||||
import { create } from 'zustand';
|
||||
import { APP_CONFIG } from '@/config/app';
|
||||
import { fetchAppConfig } from '@/services/shareApi';
|
||||
|
||||
interface AppConfigState {
|
||||
name: string;
|
||||
maxNodes: number;
|
||||
maxMapSizeMb: number;
|
||||
/** Faux tant que le backend n'a pas repondu : l'app reste utilisable hors ligne. */
|
||||
serverReachable: boolean;
|
||||
load: () => Promise<void>;
|
||||
}
|
||||
|
||||
/**
|
||||
* Le nom du produit vient de la configuration serveur (APP_NAME), avec repli
|
||||
* sur `APP_CONFIG.name`. Aucun composant ne doit ecrire le nom en dur.
|
||||
*/
|
||||
export const useAppConfigStore = create<AppConfigState>((set) => ({
|
||||
name: APP_CONFIG.name,
|
||||
maxNodes: APP_CONFIG.maxNodes,
|
||||
maxMapSizeMb: APP_CONFIG.maxMapSizeMb,
|
||||
serverReachable: false,
|
||||
|
||||
load: async () => {
|
||||
try {
|
||||
const config = await fetchAppConfig();
|
||||
set({
|
||||
name: config.appName || APP_CONFIG.name,
|
||||
maxNodes: config.maxNodes || APP_CONFIG.maxNodes,
|
||||
maxMapSizeMb: config.maxMapSizeMb || APP_CONFIG.maxMapSizeMb,
|
||||
serverReachable: true,
|
||||
});
|
||||
if (typeof document !== 'undefined') {
|
||||
document.title = config.appName || APP_CONFIG.name;
|
||||
}
|
||||
} catch {
|
||||
// Hors ligne : on garde les valeurs par defaut, sans bloquer l'interface.
|
||||
set({ serverReachable: false });
|
||||
}
|
||||
},
|
||||
}));
|
||||
Reference in New Issue
Block a user