Le projet et ses formats
Le projet de l'élève est le dossier /workspace (fichiers et dépôt .git), dans l'IndexedDB du navigateur. Cette page décrit l'objet qui le représente, son cycle de vie et les trois formats par lesquels il entre et sort de CapyCode : le projet de départ, le lien de partage et l'archive ZIP.
L'objet Workspace
Workspace (src/services/workspace.ts) réunit le système de fichiers (fs, un VirtualFileSystem) et le dépôt (git, un GitService) d'un projet :
| Membre | Rôle |
|---|---|
root | racine (/workspace) |
path(rel), relative(path) | conversions entre chemin relatif (celui de Git, des liens, des ZIP) et absolu |
setAuthor({ name, email }) | identité des commits (aujourd'hui une identité par défaut ; demain celle de l'utilisateur Capytale) |
batch(task) | lot de fichiers et de Git : une seule notification de chaque à la fin |
L'application a un projet, workspace. Les services de projet (src/services/project/) reçoivent le Workspace sur lequel agir au lieu d'importer les singletons : ils marchent sur un projet de test, et marcheront sur un projet stocké ailleurs (par Capytale) le jour venu.
Le cycle de vie
Ensuite, à tout moment, depuis le menu Fichier ou la palette (useProject) :
- Nouveau projet : purge du workspace après confirmation ; le projet reste vide (sans dépôt Git).
- Exporter :
exportArchive→ téléchargement decapycode-project.zip. - Importer une archive (menu, ou ZIP déposé sur la fenêtre) :
importArchive, après confirmation. - Déposer des fichiers (autres qu'un ZIP) sur la fenêtre : ils sont ajoutés au projet.
- Partager :
ShareModalconstruit un lien ; ouvrir un lien dans un onglet déjà ouvert le charge aussi (hashchange).
useProject (composable) porte les confirmations, messages et fichier à ouvrir ; les services de src/services/project/ font le travail sur le Workspace. Les stores suivent seuls, par les notifications.
Purge complète
Importer un ZIP ou un instantané purge entièrement le workspace avant d'écrire (purgeWorkspace, qui notifie un reset) : il ne reste rien de l'ancien projet, ni fichier, ni branche. L'instantané mémoire de Pyodide, lui, est dans une autre base IndexedDB et n'est pas touché.
Le projet de départ
src/services/project/defaults.ts. À la toute première visite (pas de préférence capycode_seeded, pas de dépôt), CapyCode crée un dépôt sur main, un main.py (texte dans la langue de l'interface) et un premier commit.
Ce projet est reconnu ensuite (hasCustomContent) : tant qu'il est intact, ouvrir un lien de partage le remplace sans demander de confirmation. La préférence capycode_seeded évite de le recréer quand l'élève a volontairement vidé son projet.
L'instantané et le lien de partage
src/services/project/snapshot.ts et shareLink.ts.
- Construction (
ShareModal) :collectFiles(fichiers texte, hors fichiers cachés ; il renvoie à part les fichiers binaires écartés), la liste des extensions activées choisies par l'utilisateur, et au choixcollectCommits(les n derniers commits de la branche active ou de toutes les branches, le premier avec son arborescence, les suivants en différences) etcollectBranches. - Lien :
encodeShareLinkcompresse le JSON avec fflate (DEFLATE au niveau maximal, encodé en Base64URL) dans le fragment#capycode=…, jamais envoyé au serveur. Le format ne transporte que du texte, et un lien doit rester court : il est fait pour du code. Un fichier qui n'est pas de l'UTF-8 valide (decodeUtf8,src/utils/text.ts, décodage strict) est donc écarté, de l'état courant comme des commits (getCommitTreeSnapshot), plutôt que transmis corrompu ;ShareModalle signale et renvoie vers l'export ZIP. - Ouverture :
decodeShareLinkdécompresse et valide la structure (isProjectSnapshot) ; un lien illisible, tronqué ou d'une version inconnue est refusé avec un message, jamais interprété à moitié. Les chemins qui sortiraient du projet (..) sont ignorés. Les extensions spécifiées dans l'instantané sont automatiquement activées. - Restauration :
restoreSnapshotpurge le projet, rejoue les commits (mêmes messages, auteurs et dates ; les identifiants de commit, eux, changent), recrée les branches, puis écrit l'état courant des fichiers (modifications non commitées comprises), le tout dans un seulworkspace.batch.
Format versionné
SNAPSHOT_VERSION vaut 2. Toute évolution du format passe par une nouvelle version. Voir la recette.
L'archive ZIP
src/services/project/archive.ts. C'est la sauvegarde fidèle : tous les fichiers, .git compris, lus et écrits en binaire (Uint8Array). Les objets Git sont compressés : les décoder en texte les corromprait.
À l'import :
- l'archive est lue entièrement avant de toucher au projet : une archive invalide laisse le projet intact ;
- un dossier unique qui englobe tout (
projet/…, quand on zippe un dossier) est retiré ; - les fichiers ajoutés par les systèmes d'exploitation (
__MACOSX/,.DS_Store,Thumbs.db) sont ignorés, et les chemins qui sortiraient du projet neutralisés ; - le workspace est purgé puis rempli dans un seul
vfs.batch.
Où est stocké quoi
| Donnée | Où | Effacée par |
|---|---|---|
| Fichiers et dépôt du projet | IndexedDB capycode-fs (LightningFS), sous /workspace | Nouveau projet, import, lien de partage |
Préférences (thème, langue, tailles, capycode_seeded…) | localStorage, par src/utils/storage.ts | Effacement des données du site |
| Instantané mémoire de Pyodide | IndexedDB basthon-python3 (géré par Basthon) | Basthon, quand Pyodide change de version |
| Interface, noyaux, Ruff | Caches du service worker | Nouvelle version déployée |
Rien n'est envoyé à un serveur : le projet n'existe que dans ce navigateur, d'où l'importance de l'export ZIP (et, à terme, d'une sauvegarde par Capytale).