7.0 KiB
Architecture
Pena (Pena-tauri) est une application Tauri 2 : un backend Rust
expose des commandes à un frontend web (HTML/CSS/JS vanilla, sans bundler). Le frontend
est servi en fichiers statiques depuis src/, le backend est compilé dans src-tauri/.
L'ensemble suit une Clean Architecture stricte, côté Rust comme côté JS.
Vue d'ensemble
┌──────────────────────────── Frontend (src/, JS vanilla) ────────────────────────────┐
│ ui/ — composants d'affichage (home, sidebar, reader, css-modal) │
│ state/ — état global minimal (app-state) │
│ services/ — appels Tauri via invoke (markdown, files, watcher, themes) │
│ router.js — navigation entre vues (accueil / lecteur) │
└──────────────────────────────────────┬───────────────────────────────────────────────┘
│ window.__TAURI__.core.invoke(...)
│ window.__TAURI__.event.listen(...)
┌──────────────────────────────────────┴──── Backend (src-tauri/src/, Rust) ───────────┐
│ commands/ — points d'entrée #[tauri::command] (render, watch, theme) │
│ application/ — services métier (render_service, file_service, theme_service) │
│ infrastructure/ — implémentations concrètes (comrak, notify, fs, thèmes statiques) │
│ domain/ — entités et traits purs (MarkdownRenderer, ThemeRepository…) │
│ lib.rs — point d'assemblage (wiring + enregistrement des commandes) │
└───────────────────────────────────────────────────────────────────────────────────────┘
Backend Rust — couches
Le code Rust (src-tauri/src/) est découpé en quatre couches, avec des règles de dépendance
strictes.
| Couche | Rôle | Peut dépendre de | Ne doit PAS dépendre de |
|---|---|---|---|
domain/ |
Entités et traits purs, zéro dépendance externe | rien | application, infrastructure, commands |
application/ |
Services métier (orchestration des cas d'usage) | domain |
infrastructure, commands |
infrastructure/ |
Implémentations concrètes des traits du domaine | domain |
application, commands |
commands/ |
Commandes exposées à Tauri | application, domain |
infrastructure (directement) |
domain/
Définit les contrats, sans aucune dépendance technique :
markdown.rs— traitMarkdownRenderer { fn render(&self, content: &str) -> String }et structRenderOptions(tables, strikethrough, autolink, tasklist).theme.rs— structTheme { id, label }et traitThemeRepository { fn list() -> Vec<Theme>; fn get_css(id) -> Option<String> }.
application/
Orchestration métier, dépend uniquement du domaine :
render_service.rs—render_string()(rendu d'une chaîne) etrender_file()(lecture du fichier puis rendu).file_service.rs—list_markdown_files(): valide le répertoire, collecte les.md, trie alphabétiquement.theme_service.rs—list_themes()etget_theme_css(), délégués au repository.
infrastructure/
Implémentations concrètes des traits du domaine :
comrak_renderer.rs—ComrakRenderer(toutes les extensions activées, pour les fichiers) etComrakPreviewRenderer(sans extensions, pour les aperçus rapides). Fournit aussisyntax_css_for_theme()pour générer le CSS de colorationsyntect. La syntaxe TypeScript est chargée depuisresources/syntaxes/TypeScript.sublime-syntax.file_repository.rs—collect_md_files()(parcours récursif, ignore les fichiers cachés, ne retient que les.md) etread_file().notify_watcher.rs— boucle de débounce (80 ms) qui filtre les événements de la cratenotifyet émet l'événement Taurifile-changed.theme_repository.rs—StaticThemeRepository: les 6 thèmes et leur CSS sont intégrés au binaire viainclude_str!()(fichiersresources/themes/*.css).
commands/
Points d'entrée Tauri qui instancient les implémentations concrètes et appellent les services. Voir la référence complète : Commandes Tauri.
lib.rs — point d'assemblage
Seul endroit où les implémentations concrètes sont câblées. Il :
- enregistre les plugins
tauri-plugin-fsettauri-plugin-dialog; - gère l'état global
WatcherState(un seul watcher actif à la fois) ; - enregistre les 8 commandes via
generate_handler![].
Frontend JS — couches
Le code JS (src/) suit le même esprit de séparation :
| Dossier | Rôle | Peut dépendre de |
|---|---|---|
ui/ |
Composants d'affichage, sans logique métier ni appel Tauri direct | services/, state/ |
state/ |
État global de l'application (app-state.js) |
— |
services/ |
Appels Tauri (invoke) et logique de données |
API Tauri |
router.js |
Navigation entre les vues (accueil / lecteur), orchestrateur | services/, ui/, state/ |
Règle importante : les composants ui/ ne font jamais d'appel Tauri directement ;
ils passent toujours par un service de services/.
Services frontend
services/markdown.js—render_markdown,convert_fileservices/files.js—list_md_filesservices/watcher.js—start_watch,stop_watch, écoute defile-changed(présent mais non utilisé par l'UI)services/themes.js—list_themes,get_theme_css
Dépendances Rust principales
| Crate | Rôle |
|---|---|
tauri (+ tauri-plugin-fs, tauri-plugin-dialog) |
Framework applicatif, IPC, événements, dialogues |
comrak |
Rendu CommonMark + extensions |
syntect |
Coloration syntaxique des blocs de code |
notify |
Surveillance du système de fichiers |
serde / serde_json |
Sérialisation des DTO et payloads |
Ressources intégrées au binaire
| Ressource | Emplacement | Usage |
|---|---|---|
| Thèmes rapides | src-tauri/resources/themes/*.css |
Servis par get_theme_css |
| Syntaxe TypeScript | src-tauri/resources/syntaxes/TypeScript.sublime-syntax |
Coloration des blocs TS |
Elles sont incluses à la compilation (include_str!), donc l'application n'a pas besoin de
fichiers externes pour fonctionner.