Chaque PaaS a un tableau de bord. La plupart ressemblent à des interfaces construites en 2018 et jamais mises à jour. Nous voulions que le nôtre donne l'impression d'un produit qu'on prendrait plaisir à utiliser -- mode sombre, cinq langues, logs en temps réel, responsive jusqu'au mobile -- et nous voulions qu'il soit compilé dans le binaire Rust pour que sh0 soit livré comme un seul exécutable avec zéro dépendance externe.
Le 12 mars 2026, nous sommes passés d'un dossier dashboard/ vide à une SPA SvelteKit entièrement fonctionnelle avec 7 pages, 8 composants UI partagés, un système i18n couvrant cinq langues, et un client API typé gérant l'authentification, les états d'erreur et la reconnexion WebSocket. Puis nous avons embarqué le tout dans notre binaire Rust avec include_dir. Quarante-huit heures plus tard, chaque page avait de vraies données.
Pourquoi Svelte 5 et pas React
La décision a pris environ trente secondes. Les Runes ($state, $derived, $effect, $props) ont éliminé la cérémonie de stores que React exige. Pas de danse useState / useEffect / useCallback / useMemo. Pas de bugs de tableau de dépendances. Déclarez votre état et utilisez-le.
SvelteKit 2 avec adapter-static nous a donné le routage basé sur les fichiers et une étape de build qui produit du HTML, CSS et JavaScript brut -- exactement ce dont nous avions besoin pour embarquer dans un binaire Rust.
L'échafaudage de la Phase 12
L'échafaudage a livré huit éléments en une seule session : TailwindCSS 4 avec thème personnalisé, le système i18n en 5 langues, le store d'auth et le flux de connexion, le client API, le client WebSocket avec reconnexion automatique, huit composants UI partagés, la mise en page sidebar responsive, et sept pages placeholder.
La Phase 13 : les pages principales prennent vie
Le tableau de bord d'accueil montre quatre cartes de statistiques et une liste de déploiements récents. La liste d'applications est un grille de cartes avec recherche, filtrage et un modal « Créer une application ». La page de détail d'application est la plus complexe : une seule route avec six onglets (Vue d'ensemble, Déploiements, Logs, Domaines, Environnement, Paramètres).
Le LogViewer mérite une mention spéciale. Il ouvre une connexion WebSocket, affiche les lignes entrantes dans un conteneur monospace avec défilement automatique, et maintient un buffer de 1 000 lignes pour empêcher la croissance mémoire.
La Phase 14 : pages étendues
La poussée finale a remplacé les quatre pages placeholder restantes par des implémentations de production : Bases de données, Sauvegardes, Monitoring et Paramètres (avec configuration TOTP et gestion des clés API).
Embarquer le tableau de bord dans un binaire Rust
La crate include_dir a permis d'embarquer le répertoire dashboard/build entier dans le binaire compilé au moment du build. Le handler static_files.rs sert les fichiers depuis le répertoire embarqué, avec un repli SPA vers index.html.
Le résultat : sh0 est un binaire unique. Téléchargez-le, lancez-le, ouvrez un navigateur. Le tableau de bord est déjà là. Pas de npm install. Pas de serveur frontend séparé.
Ce que nous avons appris
Les runes Svelte 5 sont un vrai bond en avant. Le modèle $state / $derived / $effect correspond directement à la façon dont on pense l'interface. TailwindCSS 4 avec propriétés CSS personnalisées est la bonne approche de thème. L'i18n dès le premier jour ne coûte presque rien. Embarquer une SPA dans un binaire Rust se fait étonnamment bien.
En 48 heures, nous sommes passés d'un répertoire vide à un tableau de bord avec 11 pages fonctionnelles, 19 composants personnalisés, 5 langues, thèmes sombre/clair, streaming de logs en temps réel, et variables d'environnement chiffrées -- le tout compilé dans un seul binaire Rust.
Prochain dans la série : Des listes plates aux Stacks : repenser toute notre UX.