Skip to content
English

Vue d'ensemble ​

CapyCode reproduit l'ergonomie de VS Code (explorateur, onglets, éditeur, terminal, Git) pour des élèves débutants. Tout tourne dans le navigateur : il n'y a pas de serveur applicatif, ni pour stocker les projets, ni pour exécuter le code.

Les couches ​

  • Services (src/services/) : la logique technique, sans dépendance à Vue ni aux stores. Ils sont testables directement sous Node.
  • Stores (src/stores/) : l'état affiché par l'interface. Ils appellent les services et s'abonnent à leurs notifications.
  • Composants (src/components/) : l'affichage. Ils lisent les stores et déclenchent leurs actions eux-mêmes, plutôt que de recevoir des copies de l'état en props et de remonter des événements que le parent rejouerait. Un grand composant se découpe en sous-composants rangés dans un dossier à son nom (editor/git-graph/ pour GitGraphView.vue).
  • Composables (src/composables/) : la logique d'interface partagée entre composants ou sortie d'un composant trop chargé. useProject gère le cycle de vie du projet (démarrage, activité partagée, ZIP), useEscapeKey la fermeture de la fenêtre au premier plan, usePointerDrag et useSeparator les poignées de redimensionnement (à la souris et au clavier), useTreeNavigation et useTabEscape la navigation au clavier (voir Accessibilité), useGitActions les actions Git de l'interface (avec confirmations et messages) communes à la barre latérale, au graphe et aux comparaisons, useCopyFeedback la copie avec retour visuel. App.vue ne fait plus qu'assembler l'interface.
  • Commandes (src/commands/) : les actions de l'IDE, décrites une fois ; menus, palette et raccourcis en dérivent. Voir Commandes.
  • Langages (src/languages/) : tout ce que l'IDE sait d'un langage, entre les services et les stores. Voir Langages.

ESLint fait respecter l'ordre des couches : chacune n'importe que les précédentes.

Dialogues et notifications ​

Les dialogues natifs du navigateur (window.confirm, alert, prompt) sont interdits (ESLint) : ils ne sont ni accessibles, ni traduisibles, ni testables, et sont bloqués dans certaines iframes. À la place, le store dialogs :

  • await dialogs.confirm({ title, message, confirmLabel, danger }) : vrai si l'utilisateur confirme. Une action destructrice (danger) a son bouton en rouge et le focus initial sur Annuler.
  • await dialogs.prompt({ title, label, initialValue, validate }) : le texte saisi, ou null.
  • dialogs.notify({ kind, message }) et dialogs.notifyError(message, err) : messages non bloquants. Les informations disparaissent après quelques secondes (sauf si on les survole ou les focalise), les erreurs restent jusqu'à leur fermeture.

DialogHost.vue (monté une fois dans App.vue) les affiche : une fenêtre modale à la fois (BaseDialog.vue : aria-modal, focus piégé puis rendu, Échap), et deux zones de notification toujours présentes (role="status", role="alert") pour que les lecteurs d'écran annoncent chaque message. Les nouvelles fenêtres modales s'appuient sur BaseDialog.

Les erreurs ​

Une erreur que l'on veut reconnaître est une classe de src/utils/errors.ts, avec un code stable et un message déjà traduit, destiné à l'utilisateur :

ClasseCodeExemple
MergeConflictErrormerge-conflictCommit de fusion refusé : des conflits restent à résoudre ; files donne les fichiers concernés
InvalidInputErrorinvalid-inputMessage de commit vide, déplacement d'un dossier dans lui-même
NotFoundErrornot-foundFichier introuvable ; path donne le chemin

On les reconnaît par instanceof (ou error.code), jamais en comparant un message. L'erreur d'origine (isomorphic-git, LightningFS) est gardée dans cause. Une erreur attrapée est de type unknown : errorMessage(err) en donne le texte, errorCode(err) le code d'une erreur de bibliothèque (git.Errors.MergeConflictError.code).

Les briques techniques et pourquoi elles ​

BesoinChoixPourquoi
InterfaceVue 3, Pinia, TailwindComposants légers, réactivité fine, thème VS Code par classes utilitaires
ÉditeurCodeMirror 6Modulaire et léger (contrairement à Monaco), bon support mobile
FichiersLightningFS (@isomorphic-git/lightning-fs)Système de fichiers persistant dans IndexedDB, celui qu'attend isomorphic-git
Gitisomorphic-gitGit complet en JavaScript, sur le même système de fichiers
Analyse PythonRuff (WASM)Lint et formatage en un seul module, assez rapide pour tourner à chaque frappe ; chargé au premier fichier Python
ExécutionNoyaux Basthon (Python, SQL, OCaml)Noyaux pédagogiques maintenus, isolés dans des workers ; voir Intégration de Basthon
ProjetsJSZip, fflateExport/import ZIP avec l'historique .git, partage d'une activité dans l'URL

Le workspace ​

Le projet de l'élève vit sous /workspace (WORKSPACE_ROOT, src/utils/path.ts) dans LightningFS, avec son dépôt Git dans /workspace/.git. Trois règles :

  • Binaire avant tout. Les objets Git sont compressés : ils se lisent et s'écrivent en Uint8Array, jamais en texte.
  • Une seule porte d'entrée. Toute modification passe par le service VFS (src/services/vfs.ts) ou par le service Git, qui notifient les stores. C'est ce qui garde l'interface cohérente : voir Stores et synchronisation.
  • Jamais /workspace en dur. Les chemins se convertissent avec resolveWorkspacePath / getRelativeWorkspacePath (src/utils/path.ts) ; le chemin vu par Git est le chemin relatif.

Workspace (src/services/workspace.ts) réunit le système de fichiers et le dépôt d'un projet, avec l'identité des commits (setAuthor) et les lots (batch). Le projet de l'application est workspace ; les services de projet reçoivent le Workspace sur lequel agir, ce qui les rend utilisables sur un autre projet (tests, projet stocké ailleurs).

Le projet comme un tout ​

src/services/project/ traite le projet entier : projet de départ (defaults.ts), instantané versionné et lien de partage #capycode=… (snapshot.ts, shareLink.ts), archive ZIP fidèle, .git compris (archive.ts). Le format d'instantané porte un numéro de version : toute évolution passe par une nouvelle version. Voir Le projet et ses formats.

Le service Git ​

src/services/git/ compose trois classes : GitRepository (lecture), GitInspection (contenus, diffs) et GitService (opérations et notifications), réunies dans le singleton gitService. Le terminal comme l'interface passent par lui. Voir Git.

L'éditeur et l'analyse du code ​

CodeMirror 6, deux volets d'onglets, comparaisons, recherche, palette ; analyse et formatage Python par Ruff en WebAssembly, chargé à la demande. Voir Éditeur et Analyse Python.

L'exécution du code ​

Le code de l'élève s'exécute dans les noyaux Basthon, chacun dans un worker isolé (origine opaque) : il ne peut lire ni le workspace, ni l'état de l'IDE. Le noyau est choisi selon l'extension du fichier (.py, .sql, .ml). Voir Intégration de Basthon.

L'intégration dans une page hôte ​

Capytale intègre CapyCode dans un cadre et peut régler certaines fonctions (désactiver Git) par postMessage, à tout moment : src/services/embedding.ts porte le protocole, le store embedding les options. Voir Intégration dans une page hôte.

Le déploiement ​

npm run build produit un site statique (dist/), déployé par la CI GitLab avec sa configuration Apache (public/.htaccess). L'application est aussi une PWA : le service worker (src/sw.ts) précache l'interface, mais charge toujours la page depuis le réseau quand il le peut, pour qu'une mise à jour soit visible dès le rechargement suivant ; les noyaux Basthon et Ruff sont mis en cache à leur première utilisation. Voir Déploiement.