refactor docs into feature folder

This commit is contained in:
2026-04-28 13:54:41 +02:00
parent fc5e4acba4
commit d2735b72a0
15 changed files with 18 additions and 18 deletions
-51
View File
@@ -1,51 +0,0 @@
import ReactMarkdown from "react-markdown";
import remarkGfm from "remark-gfm";
import { useDocsLanguage } from "@/pages/docs/useDocsLanguage";
interface DocsDocumentProps {
title: string;
meta: string;
content: string;
frContent: string;
}
export function DocsDocument({
title,
meta,
content,
frContent,
}: DocsDocumentProps): React.JSX.Element {
const { language, toggleLanguage } = useDocsLanguage();
const translatedContent = language === "fr" ? frContent : content;
return (
<div className="docs-content">
<header className="docs-content__header">
<span>{title}</span>
<button
className="docs-language-toggle"
type="button"
onClick={toggleLanguage}
aria-label="Changer la langue de la documentation"
>
<span className={language === "fr" ? "is-active" : undefined}>
FR
</span>
<span className={language === "en" ? "is-active" : undefined}>
EN
</span>
</button>
</header>
<article className="docs-section">
<div className="docs-section__eyebrow">
<span>{title}</span>
<span>{meta}</span>
</div>
<ReactMarkdown remarkPlugins={[remarkGfm]}>
{translatedContent}
</ReactMarkdown>
</article>
</div>
);
}
-25
View File
@@ -1,25 +0,0 @@
import { useState } from "react";
import {
DocsLanguageContext,
type DocsLanguage,
} from "@/pages/docs/docsLanguageContext";
interface DocsLanguageProviderProps {
children: React.ReactNode;
}
export function DocsLanguageProvider({
children,
}: DocsLanguageProviderProps): React.JSX.Element {
const [language, setLanguage] = useState<DocsLanguage>("en");
function toggleLanguage(): void {
setLanguage((currentLanguage) => (currentLanguage === "en" ? "fr" : "en"));
}
return (
<DocsLanguageContext value={{ language, toggleLanguage }}>
{children}
</DocsLanguageContext>
);
}
-53
View File
@@ -1,53 +0,0 @@
import { Link, Outlet } from "@tanstack/react-router";
import { Home } from "lucide-react";
import { DocsLanguageProvider } from "@/pages/docs/DocsLanguageProvider";
import { docGroups } from "@/pages/docs/docsSections";
export function DocsLayout(): React.JSX.Element {
return (
<DocsLanguageProvider>
<main className="docs-page">
<aside className="docs-sidebar" aria-label="Documentation">
<header className="docs-sidebar__header">
<h1>Folders</h1>
<Link
className="docs-home-link"
to="/"
aria-label="Retour à l'accueil"
>
<Home size={18} strokeWidth={2.25} aria-hidden="true" />
</Link>
</header>
<nav>
{docGroups.map((group) => (
<section className="docs-nav-group" key={group.label}>
<h2>{group.label}</h2>
{group.sections.map((section) => (
<Link
activeProps={{
className: "docs-nav-item docs-nav-item--active",
}}
activeOptions={{ exact: true }}
className="docs-nav-item"
key={section.path}
to={section.path}
>
<span>
<strong>{section.title}</strong>
<small>{section.subtitle}</small>
</span>
<span className="docs-nav-item__meta">{section.meta}</span>
</Link>
))}
</section>
))}
</nav>
</aside>
<Outlet />
</main>
</DocsLanguageProvider>
);
}
-99
View File
@@ -1,99 +0,0 @@
import { Suspense, lazy } from "react";
const LazyDocsLayout = lazy(() =>
import("@/pages/docs/DocsLayout").then((module) => ({
default: module.DocsLayout,
})),
);
const LazyDocsReadmePage = lazy(() =>
import("@/pages/docs/page").then((module) => ({
default: module.DocsReadmePage,
})),
);
const LazyDocsArchitecturePage = lazy(() =>
import("@/pages/docs/architecture/page").then((module) => ({
default: module.DocsArchitecturePage,
})),
);
const LazyDocsTargetArchitecturePage = lazy(() =>
import("@/pages/docs/target-architecture/page").then((module) => ({
default: module.DocsTargetArchitecturePage,
})),
);
const LazyDocsTechnicalEditorPage = lazy(() =>
import("@/pages/docs/technical-editor/page").then((module) => ({
default: module.DocsTechnicalEditorPage,
})),
);
const LazyDocsFeaturesPage = lazy(() =>
import("@/pages/docs/features/page").then((module) => ({
default: module.DocsFeaturesPage,
})),
);
const LazyDocsEditorPage = lazy(() =>
import("@/pages/docs/editor/page").then((module) => ({
default: module.DocsEditorPage,
})),
);
export function DocsLayoutRoute(): React.JSX.Element {
return (
<Suspense fallback={null}>
<LazyDocsLayout />
</Suspense>
);
}
export function DocsReadmeRoute(): React.JSX.Element {
return (
<Suspense fallback={null}>
<LazyDocsReadmePage />
</Suspense>
);
}
export function DocsArchitectureRoute(): React.JSX.Element {
return (
<Suspense fallback={null}>
<LazyDocsArchitecturePage />
</Suspense>
);
}
export function DocsTargetArchitectureRoute(): React.JSX.Element {
return (
<Suspense fallback={null}>
<LazyDocsTargetArchitecturePage />
</Suspense>
);
}
export function DocsTechnicalEditorRoute(): React.JSX.Element {
return (
<Suspense fallback={null}>
<LazyDocsTechnicalEditorPage />
</Suspense>
);
}
export function DocsFeaturesRoute(): React.JSX.Element {
return (
<Suspense fallback={null}>
<LazyDocsFeaturesPage />
</Suspense>
);
}
export function DocsEditorRoute(): React.JSX.Element {
return (
<Suspense fallback={null}>
<LazyDocsEditorPage />
</Suspense>
);
}
+2 -2
View File
@@ -1,6 +1,6 @@
import architecture from "../../../../docs/technical/architecture.md?raw";
import { DocsDocument } from "@/pages/docs/DocsDocument";
import { architectureFr } from "@/pages/docs/docsTranslations";
import { DocsDocument } from "@/features/docs/components/DocsDocument";
import { architectureFr } from "@/features/docs/data/docsTranslations";
export function DocsArchitecturePage(): React.JSX.Element {
return (
-11
View File
@@ -1,11 +0,0 @@
import { createContext } from "react";
export type DocsLanguage = "en" | "fr";
export interface DocsLanguageContextValue {
language: DocsLanguage;
toggleLanguage: () => void;
}
export const DocsLanguageContext =
createContext<DocsLanguageContextValue | null>(null);
-60
View File
@@ -1,60 +0,0 @@
export interface DocSection {
path: string;
title: string;
subtitle: string;
meta: string;
}
export interface DocGroup {
label: string;
sections: DocSection[];
}
export const docGroups: DocGroup[] = [
{
label: "Technical",
sections: [
{
path: "/docs",
title: "README",
subtitle: "Project overview",
meta: "01",
},
{
path: "/docs/architecture",
title: "Current Architecture",
subtitle: "Runtime structure",
meta: "02",
},
{
path: "/docs/target-architecture",
title: "Target Architecture",
subtitle: "Next direction",
meta: "03",
},
{
path: "/docs/technical-editor",
title: "Editor Technical Notes",
subtitle: "Implementation details",
meta: "04",
},
],
},
{
label: "User",
sections: [
{
path: "/docs/features",
title: "Features",
subtitle: "Implemented scope",
meta: "05",
},
{
path: "/docs/editor",
title: "Editor User Guide",
subtitle: "Editing workflow",
meta: "06",
},
],
},
];
-328
View File
@@ -1,328 +0,0 @@
export const readmeFr = `# La-Fabrik
Une expérience web 3D interactive pour La Fabrik Durable, un service low-tech de réparation et de transformation situé à Altera, une ville post-capitaliste reconstruite en 2039. Les joueurs incarnent un technicien fraîchement intégré et vivent une journée de service : réparer un vélo électrique, remettre en état un réseau d'énergie et améliorer le système d'irrigation d'une ferme verticale.
Construit avec React, Three.js et Vite. Fonctionne dans le navigateur, sans installation côté utilisateur.
## Stack technique
### Build et langage
| Package |
| -------------------------------------------------- |
| [TypeScript](https://www.typescriptlang.org/docs/) |
| [React](https://react.dev/learn) |
| [Vite](https://vite.dev/guide/) |
| [ESLint](https://eslint.org/docs/latest/) |
| [Prettier](https://prettier.io/docs/) |
### Moteur 3D
| Package |
| ----------------------------------------------------------------------------------------- |
| [Three.js](https://threejs.org/docs/) |
| [@react-three/fiber](https://docs.pmnd.rs/react-three-fiber/getting-started/introduction) |
| [@react-three/drei](https://pmndrs.github.io/drei) |
| [@react-three/rapier](https://rapier.rs/docs/) |
| [@react-three/postprocessing](https://github.com/pmndrs/postprocessing) |
| [GSAP](https://gsap.com/docs/v3/Installation/) |
### Performance et effets
| Package |
| --------------------------------------------------------------------------- |
| [r3f-perf](https://github.com/utsuboco/r3f-perf) |
| [AnimationMixer](https://threejs.org/docs/#api/en/animation/AnimationMixer) |
## Structure du projet
\`\`\`
la-fabrik/
├── public/
│ ├── models/
│ │ ├── map/ # Carte de base, chargée au démarrage
│ │ ├── workshop/
│ │ ├── powerGrid/
│ │ └── farm/
│ ├── textures/
│ └── sounds/
└── src/
├── world/ # Monde 3D persistant
│ ├── World.tsx # Composition principale de la scène
│ ├── Map.tsx # Carte de base, toujours montée
│ ├── Lighting.tsx # Lumières ambiante, directionnelle et ponctuelles
│ ├── Environment.tsx # HDRI, brouillard, ciel
│ ├── PostFX.tsx # Bloom, SSAO, aberration chromatique
│ ├── zones/ # Zones spatiales, LOD par zone
│ └── player/ # Contrôleur joueur et caméra
├── components/
│ ├── 3d/ # Éléments 3D réutilisables
│ └── ui/ # Overlays HTML hors Canvas
├── stateManager/ # Logique, état et orchestration
├── hooks/ # Hooks React autour des managers
├── data/ # Configuration statique
├── shaders/ # Shaders GLSL
└── utils/ # Utilitaires partagés et debug
\`\`\`
## Démarrage
\`\`\`bash
git clone https://github.com/La-Fabrik-Durable/La-Fabrik.git
cd La-Fabrik
npm install
npm run dev
\`\`\`
- application : \`http://localhost:5173\`
- mode debug : \`http://localhost:5173?debug\`
## Licence
Voir le fichier [LICENSE](./LICENSE).
`;
export const architectureFr = `# Architecture actuelle
Ce document décrit le code réellement présent aujourd'hui dans le dépôt.
## Structure runtime
- \`src/App.tsx\` monte le \`Canvas\`, le \`World\` 3D, l'overlay de performance debug et les overlays HTML.
- \`src/world/World.tsx\` compose la scène active avec :
- l'environnement et l'éclairage
- les helpers debug et le mode caméra debug
- soit la carte principale, soit la scène de test physique debug
- le rig joueur quand le mode caméra actif est \`player\`
- \`src/world/Map.tsx\` charge le modèle principal de la carte et construit l'octree de collision.
- \`src/world/debug/TestScene.tsx\` fournit une scène orientée debug pour les interactions et la physique.
- \`src/world/player/PlayerComponent.tsx\` monte la caméra et le contrôleur.
- \`src/world/player/PlayerController.tsx\` gère le mouvement pointer lock, le saut et les inputs d'interaction.
## Modèle d'interaction
- \`src/stateManager/InteractionManager.ts\` est la source d'état actuelle des interactions.
- \`src/components/3d/InteractableObject.tsx\` gère la détection de focus par distance et raycasting.
- \`src/components/3d/TriggerObject.tsx\` implémente les interactions de type trigger.
- \`src/components/3d/GrabbableObject.tsx\` implémente les interactions saisir / relâcher.
- \`src/hooks/useInteraction.ts\` expose un snapshot d'interaction à l'UI React.
- \`src/components/ui/InteractPrompt.tsx\` affiche le prompt \`E\` pour les interactions trigger.
## Audio
- \`src/stateManager/AudioManager.ts\` fournit actuellement une lecture de sons one-shot avec pool.
- Les interactions trigger peuvent lancer directement un son via \`AudioManager\`.
## Système debug
- Le mode debug est activé avec \`?debug\`.
- \`src/utils/debug/Debug.ts\` possède l'instance \`lil-gui\` et les contrôles debug.
- \`src/hooks/debug/useCameraMode.ts\` et \`src/hooks/debug/useSceneMode.ts\` s'abonnent à l'état debug.
- \`src/utils/debug/DebugPerf.tsx\` monte \`r3f-perf\` en lazy uniquement en mode debug.
- \`src/utils/debug/scene/DebugHelpers.tsx\` monte les helpers debug.
- \`src/utils/debug/scene/DebugCameraControls.tsx\` monte la caméra libre debug.
## Limites actuelles
- Le dépôt est encore un prototype, pas le runtime complet du jeu.
- \`src/world/debug/TestScene.tsx\` fait encore partie de la composition active.
- Il n'existe pas encore d'orchestrateur gameplay central comme \`GameManager\`.
- Les systèmes de missions, zones, cinématiques et dialogues ne sont pas implémentés.
- Le joueur utilise une collision octree et des règles simples, pas une pile physique gameplay complète.
`;
export const targetArchitectureFr = `# Architecture cible
Ce document décrit l'architecture visée à moyen terme pour le projet.
## Relation avec le code actuel
- \`docs/technical/architecture.md\` reste la source de vérité de ce qui existe maintenant.
- Ce document est volontairement aspirational.
- Si ce document contredit l'implémentation actuelle, l'implémentation actuelle gagne.
## Objectifs
- Garder \`App.tsx\` petit et centré sur l'orchestration.
- Séparer le code de production du monde des chemins runtime uniquement debug.
- Garder une source de vérité claire par responsabilité.
- Faire grandir les systèmes gameplay progressivement, sans préconstruire une architecture vide.
## Couches prévues
### Couche App
- \`App.tsx\` monte la scène canvas et les overlays HTML de premier niveau.
- Il doit rester fin et éviter la logique gameplay.
### Couche World
- \`src/world/\` doit contenir la composition de scène de production et les objets de scène de production.
- Responsabilités attendues :
- composition du monde
- carte, environnement, éclairage
- contrôleur joueur
- ancres d'interaction de production
- post-processing de production si nécessaire
### Couche Debug
- Les scènes et outils uniquement debug doivent être isolés du chemin de production.
- Responsabilités attendues :
- \`lil-gui\`
- overlay de performance
- helpers de scène
- caméra libre et contrôles de calibration
- scènes temporaires de test utilisées pendant le développement
### Couche UI
- \`src/components/ui/\` doit contenir les overlays HTML visibles par le joueur.
- Exemples futurs :
- crosshair
- flow de chargement
- HUD de mission
- overlays narratifs
### Couche Gameplay
- À mesure que le projet grandit, l'état gameplay peut évoluer vers une couche d'orchestration plus claire.
- Sujets probables :
- missions
- zones
- cinématiques
- dialogues
- audio
- interactions
## Règles
- Préférer du code direct et fonctionnel plutôt qu'un échafaudage spéculatif.
- Les types partagés doivent rester proches de leur domaine jusqu'à avoir plusieurs vrais consommateurs.
- Éviter de créer de nouveaux managers ou services sans besoin runtime actif.
- Les chemins runtime uniquement debug doivent être clairement marqués et faciles à retirer plus tard.
`;
export const featuresFr = `# Fonctionnalités implémentées
Ce document liste les fonctionnalités présentes dans le code actuel.
## Scène
- Scène React Three Fiber plein écran
- Carte principale chargée depuis \`public/models/map/model.gltf\`
- Scène de test physique debug sélectionnable depuis le panneau debug
- Éclairage ambiant et directionnel
- Configuration de l'environnement de fond
## Joueur
- Mode caméra joueur
- Orientation souris avec pointer lock
- Déplacement avec \`ZQSD\`
- Saut
- Collision basée sur une octree contre la carte chargée
## Interactions
- Détection de focus par distance et raycast
- Interactions trigger activées avec \`E\`
- Interactions grab activées avec le bouton principal de la souris
- Prompt d'interaction affiché pour les interactions trigger
## Audio
- Lecture de sons one-shot pour les interactions trigger
- Pool simple par son via \`AudioManager\`
## Outils debug
- Le paramètre \`?debug\` active le panneau debug
- Contrôles \`lil-gui\` pour le mode caméra, le mode scène et les sphères d'interaction
- Helpers de scène debug
- Caméra libre debug
- Overlay \`r3f-perf\`
## Pas encore implémenté
- système de missions
- système de zones
- système de cinématiques
- système de dialogues
- flow de chargement
- minimap et HUD de mission
- séparation complète production / debug pour les scènes gameplay
`;
export const editorFr = `# Éditeur de carte
L'éditeur de carte est disponible sur "/editor". Il permet d'inspecter et d'ajuster les objets déclarés dans "/public/map.json" directement depuis le navigateur.
## Ce qui est édité
L'éditeur travaille sur la liste de nodes stockée dans "/public/map.json".
Chaque node décrit un objet de la scène :
- "name" : nom du dossier modèle dans "/public/models/{name}/model.gltf"
- "type" : catégorie de l'objet
- "position" : "[x, y, z]"
- "rotation" : "[x, y, z]"
- "scale" : "[x, y, z]"
Les modèles sont chargés depuis "/public/models". Si un modèle manque, l'éditeur affiche un cube gris de remplacement pour que le node reste sélectionnable et déplaçable.
## Workflow de base
1. Ouvrir "/editor".
2. Sélectionner un objet dans la vue 3D.
3. Choisir un mode de transformation : translation, rotation ou scale.
4. Déplacer la gizmo de transformation.
5. Utiliser undo ou redo si nécessaire.
6. Exporter le JSON mis à jour ou le sauvegarder sur le serveur de dev.
## Contrôles
| Action | Input |
| --- | --- |
| Sélectionner un objet | Clic sur l'objet |
| Désélectionner | "Esc" ou clic dans le vide |
| Mode translation | "T" |
| Mode rotation | "R" |
| Mode scale | "S" |
| Undo | "Ctrl+Z" |
| Redo | "Ctrl+Y" |
| Déplacement en vue verrouillée | "WASD", "ZQSD", flèches |
| Monter / descendre | "Space", "Shift" |
## Actions fichier
### Export JSON
"Export JSON" télécharge la liste actuelle des nodes sous le nom "map.json". À utiliser pour remplacer manuellement "/public/map.json".
### Save to server
"Save to server" est disponible uniquement en développement local. L'action écrit la carte modifiée dans "/public/map.json" via l'endpoint du serveur de dev Vite.
Cette action est masquée dans les builds de production car il n'existe pas encore d'API de persistance production.
## Inspecteur JSON
Le panneau latéral affiche le JSON brut de la carte :
- sans sélection, il affiche toute la liste des nodes
- avec un objet sélectionné, il met en évidence les lignes du node sélectionné
Utilise-le pour vérifier les valeurs numériques exactes avant export ou sauvegarde.
## Limites actuelles
- L'éditeur modifie uniquement les nodes existants.
- Il n'y a pas encore d'interface pour créer ou supprimer des objets.
- La sauvegarde production n'est pas implémentée.
- Les modèles manquants s'affichent comme cubes de fallback au lieu de bloquer tout l'éditeur.
`;
+2 -2
View File
@@ -1,6 +1,6 @@
import editor from "../../../../docs/user/editor.md?raw";
import { DocsDocument } from "@/pages/docs/DocsDocument";
import { editorFr } from "@/pages/docs/docsTranslations";
import { DocsDocument } from "@/features/docs/components/DocsDocument";
import { editorFr } from "@/features/docs/data/docsTranslations";
export function DocsEditorPage(): React.JSX.Element {
return (
+2 -2
View File
@@ -1,6 +1,6 @@
import features from "../../../../docs/user/features.md?raw";
import { DocsDocument } from "@/pages/docs/DocsDocument";
import { featuresFr } from "@/pages/docs/docsTranslations";
import { DocsDocument } from "@/features/docs/components/DocsDocument";
import { featuresFr } from "@/features/docs/data/docsTranslations";
export function DocsFeaturesPage(): React.JSX.Element {
return (
+2 -2
View File
@@ -1,6 +1,6 @@
import readme from "../../../README.md?raw";
import { DocsDocument } from "@/pages/docs/DocsDocument";
import { readmeFr } from "@/pages/docs/docsTranslations";
import { DocsDocument } from "@/features/docs/components/DocsDocument";
import { readmeFr } from "@/features/docs/data/docsTranslations";
export function DocsReadmePage(): React.JSX.Element {
return (
+2 -2
View File
@@ -1,6 +1,6 @@
import targetArchitecture from "../../../../docs/technical/target-architecture.md?raw";
import { DocsDocument } from "@/pages/docs/DocsDocument";
import { targetArchitectureFr } from "@/pages/docs/docsTranslations";
import { DocsDocument } from "@/features/docs/components/DocsDocument";
import { targetArchitectureFr } from "@/features/docs/data/docsTranslations";
export function DocsTargetArchitecturePage(): React.JSX.Element {
return (
+1 -1
View File
@@ -1,5 +1,5 @@
import technicalEditor from "../../../../docs/technical/editor.md?raw";
import { DocsDocument } from "@/pages/docs/DocsDocument";
import { DocsDocument } from "@/features/docs/components/DocsDocument";
export function DocsTechnicalEditorPage(): React.JSX.Element {
return (
-12
View File
@@ -1,12 +0,0 @@
import { useContext } from "react";
import { DocsLanguageContext } from "@/pages/docs/docsLanguageContext";
export function useDocsLanguage() {
const context = useContext(DocsLanguageContext);
if (!context) {
throw new Error("useDocsLanguage must be used inside DocsLanguageProvider");
}
return context;
}